]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Limit the number of unauthenticated manager sessions and also limit the time they...
authorMatthew Nicholson <mnicholson@digium.com>
Tue, 5 Apr 2011 14:11:46 +0000 (14:11 +0000)
committerMatthew Nicholson <mnicholson@digium.com>
Tue, 5 Apr 2011 14:11:46 +0000 (14:11 +0000)
AST-2011-005

(closes issue #18996)
Reported by: tzafrir
Tested by: mnicholson

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

configs/manager.conf.sample
main/manager.c

index 79c1ba82e59449506f858869f57eec20e582058c..7f005b8633dbddf22b9de7801e09e6f7d15b4e51 100644 (file)
@@ -25,6 +25,17 @@ enabled = no
 ;webenabled = yes
 port = 5038
 
+; authtimeout specifies the maximum number of seconds a client has to
+; authenticate.  If the client does not authenticate beofre this timeout
+; expires, the client will be disconnected. (default: 30 seconds)
+
+;authtimeout = 30
+
+; authlimit specifies the maximum number of unauthenticated sessions that will
+; be allowed to connect at any given time.
+
+;authlimit = 50
+
 ;httptimeout = 60
 ; a) httptimeout sets the Max-Age of the http cookie
 ; b) httptimeout is the amount of time the webserver waits 
index 029eb6eb986309b797d901d75b7b8b508e0b6468..f774c7e8daf348fea2d1b15c5c192cce65cde2ec 100644 (file)
@@ -119,6 +119,9 @@ struct eventqent {
 
 static AST_LIST_HEAD_STATIC(all_events, eventqent);
 
+#define DEFAULT_AUTHTIMEOUT 30
+#define DEFAULT_AUTHLIMIT 50
+
 static int displayconnects = 1;
 static int allowmultiplelogin = 1;
 static int timestampevents;
@@ -126,9 +129,12 @@ static int httptimeout = 60;
 static int broken_events_action = 0;
 static int manager_enabled = 0;
 static int webmanager_enabled = 0;
+static int authtimeout = DEFAULT_AUTHTIMEOUT;
+static int authlimit = DEFAULT_AUTHLIMIT;
 
 static int block_sockets;
 static int num_sessions;
+static int unauth_sessions = 0;
 
 static int manager_debug;      /*!< enable some debugging code in the manager */
 
@@ -206,6 +212,7 @@ struct mansession_session {
        int send_events;        /*!<  XXX what ? */
        struct eventqent *last_ev;      /*!< last event processed. */
        int writetimeout;       /*!< Timeout for ast_carefulwrite() */
+       time_t authstart;
        int pending_event;         /*!< Pending events indicator in case when waiting_thread is NULL */
        AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */
        AST_LIST_ENTRY(mansession_session) list;
@@ -1754,6 +1761,7 @@ static int action_login(struct mansession *s, const struct message *m)
                return -1;
        }
        s->session->authenticated = 1;
+       ast_atomic_fetchadd_int(&unauth_sessions, -1);
        if (manager_displayconnects(s->session))
                ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
        ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
@@ -3074,6 +3082,8 @@ static int get_input(struct mansession *s, char *output)
        int res, x;
        int maxlen = sizeof(s->session->inbuf) - 1;
        char *src = s->session->inbuf;
+       int timeout = -1;
+       time_t now;
 
        /*
         * Look for \r\n within the buffer. If found, copy to the output
@@ -3101,6 +3111,20 @@ static int get_input(struct mansession *s, char *output)
        }
        res = 0;
        while (res == 0) {
+               /* calculate a timeout if we are not authenticated */
+               if (!s->session->authenticated) {
+                       if(time(&now) == -1) {
+                               ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
+                               return -1;
+                       }
+
+                       timeout = (authtimeout - (now - s->session->authstart)) * 1000;
+                       if (timeout < 0) {
+                               /* we have timed out */
+                               return 0;
+                       }
+               }
+
                /* XXX do we really need this locking ? */
                ast_mutex_lock(&s->session->__lock);
                if (s->session->pending_event) {
@@ -3111,7 +3135,7 @@ static int get_input(struct mansession *s, char *output)
                s->session->waiting_thread = pthread_self();
                ast_mutex_unlock(&s->session->__lock);
 
-               res = ast_wait_for_input(s->session->fd, -1);   /* return 0 on timeout ? */
+               res = ast_wait_for_input(s->session->fd, timeout);
 
                ast_mutex_lock(&s->session->__lock);
                s->session->waiting_thread = AST_PTHREADT_NULL;
@@ -3144,6 +3168,7 @@ static int do_message(struct mansession *s)
        struct message m = { 0 };
        char header_buf[sizeof(s->session->inbuf)] = { '\0' };
        int res;
+       time_t now;
 
        for (;;) {
                /* Check if any events are pending and do them if needed */
@@ -3151,6 +3176,17 @@ static int do_message(struct mansession *s)
                        return -1;
                res = get_input(s, header_buf);
                if (res == 0) {
+                       if (!s->session->authenticated) {
+                               if(time(&now) == -1) {
+                                       ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
+                                       return -1;
+                               }
+
+                               if (now - s->session->authstart > authtimeout) {
+                                       ast_log(LOG_EVENT, "Client from %s, failed to authenticate in %d seconds\n", ast_inet_ntoa(s->session->sin.sin_addr), authtimeout);
+                                       return -1;
+                               }
+                       }
                        continue;
                } else if (res > 0) {
                        if (ast_strlen_zero(header_buf))
@@ -3174,13 +3210,22 @@ static int do_message(struct mansession *s)
 static void *session_do(void *data)
 {
        struct ast_tcptls_session_instance *ser = data;
-       struct mansession_session *session = ast_calloc(1, sizeof(*session));
+       struct mansession_session *session = NULL;
        struct mansession s = {.session = NULL, };
        int flags;
        int res;
 
-       if (session == NULL)
+       if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= authlimit) {
+               fclose(ser->f);
+               ast_atomic_fetchadd_int(&unauth_sessions, -1);
+               goto done;
+       }
+
+       if ((session = ast_calloc(1, sizeof(*session))) == NULL) {
+               fclose(ser->f);
+               ast_atomic_fetchadd_int(&unauth_sessions, -1);
                goto done;
+       }
 
        session->writetimeout = 100;
        session->waiting_thread = AST_PTHREADT_NULL;
@@ -3210,6 +3255,13 @@ static void *session_do(void *data)
        ast_atomic_fetchadd_int(&num_sessions, 1);
        AST_LIST_UNLOCK(&sessions);
 
+       if(time(&session->authstart) == -1) {
+               ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
+               ast_atomic_fetchadd_int(&unauth_sessions, -1);
+               destroy_session(session);
+               goto done;
+       }
+
        astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
        for (;;) {
                if ((res = do_message(&s)) < 0 || s.write_error)
@@ -3221,6 +3273,7 @@ static void *session_do(void *data)
                        ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
                ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
        } else {
+               ast_atomic_fetchadd_int(&unauth_sessions, -1);
                        if (displayconnects)
                        ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
                ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
@@ -4187,6 +4240,24 @@ static int __init_manager(int reload)
                        manager_debug = ast_true(val);
                } else if (!strcasecmp(var->name, "httptimeout")) {
                        newhttptimeout = atoi(val);
+               } else if (!strcasecmp(var->name, "authtimeout")) {
+                       int timeout = atoi(var->value);
+
+                       if (timeout < 1) {
+                               ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", var->value);
+                               authtimeout = DEFAULT_AUTHTIMEOUT;
+                       } else {
+                               authtimeout = timeout;
+                       }
+               } else if (!strcasecmp(var->name, "authlimit")) {
+                       int limit = atoi(var->value);
+
+                       if (limit < 1) {
+                               ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", var->value);
+                               authlimit = DEFAULT_AUTHLIMIT;
+                       } else {
+                               authlimit = limit;
+                       }
                } else {
                        ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
                                var->name, val);