]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Added limits to the number of unauthenticated sessions TCP based protocols are allowe...
authorMatthew Nicholson <mnicholson@digium.com>
Thu, 21 Apr 2011 18:19:21 +0000 (18:19 +0000)
committerMatthew Nicholson <mnicholson@digium.com>
Thu, 21 Apr 2011 18:19:21 +0000 (18:19 +0000)
Unrelated, the manager interface now properly checks if the user has the "system" privilege before executing shell commands via the Originate action.

AST-2011-005
AST-2011-006

(closes issue #18787)
Reported by: kobaz

(related to issue #18996)
Reported by: tzafrir

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

channels/chan_skinny.c
configs/http.conf.sample
configs/skinny.conf.sample
main/http.c
main/manager.c

index 59e8cfa790da893d0c897ef6c6fa906a6b6f2575..f6bce77d30151b5fd43a53f6075f4c1aac540a38 100644 (file)
@@ -96,8 +96,13 @@ enum skinny_codecs {
 #define DEFAULT_SKINNY_PORT    2000
 #define DEFAULT_SKINNY_BACKLOG 2
 #define SKINNY_MAX_PACKET      1000
+#define DEFAULT_AUTH_TIMEOUT   30
+#define DEFAULT_AUTH_LIMIT     50
 
 static int keep_alive = 120;
+static int auth_timeout = DEFAULT_AUTH_TIMEOUT;
+static int auth_limit = DEFAULT_AUTH_LIMIT;
+static int unauth_sessions = 0;
 static char date_format[6] = "D-M-Y";
 static char version_id[16] = "P002F202";
 
@@ -1060,6 +1065,7 @@ struct skinny_paging_device {
 static struct skinnysession {
        pthread_t t;
        ast_mutex_t lock;
+       time_t start;
        struct sockaddr_in sin;
        int fd;
        char inbuf[SKINNY_MAX_PACKET];
@@ -3064,6 +3070,7 @@ static int handle_register_message(struct skinny_req *req, struct skinnysession
                transmit_response(s, req);
                return 0;
        }
+       ast_atomic_fetchadd_int(&unauth_sessions, -1);
        if (option_verbose > 2)
                ast_verbose(VERBOSE_PREFIX_3 "Device '%s' successfully registered\n", name);
 
@@ -4427,6 +4434,9 @@ static void destroy_session(struct skinnysession *s)
                if (s->fd > -1) {
                        close(s->fd);
                }
+               if (!s->device) {
+                       ast_atomic_fetchadd_int(&unauth_sessions, -1);
+               }
                ast_mutex_destroy(&s->lock);
                free(s);
        } else {
@@ -4439,13 +4449,30 @@ static int get_input(struct skinnysession *s)
 {
        int res;
        int dlen = 0;
+       int timeout = keep_alive * 1100;
+       time_t now;
        int *bufaddr;
        struct pollfd fds[1];
 
+       if (!s->device) {
+               if(time(&now) == -1) {
+                       ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
+                       return -1;
+               }
+
+               timeout = (auth_timeout - (now - s->start)) * 1000;
+               if (timeout < 0) {
+                       /* we have timed out */
+                       if (skinnydebug)
+                               ast_verbose("Skinny Client failed to authenticate in %d seconds\n", auth_timeout);
+                       return -1;
+               }
+       }
+
        fds[0].fd = s->fd;
        fds[0].events = POLLIN;
        fds[0].revents = 0;
-       res = ast_poll(fds, 1, (keep_alive * 1100)); /* If nothing has happen, client is dead */
+       res = ast_poll(fds, 1, timeout); /* If nothing has happen, client is dead */
                                                 /* we add 10% to the keep_alive to deal */
                                                 /* with network delays, etc */
        if (res < 0) {
@@ -4454,8 +4481,13 @@ static int get_input(struct skinnysession *s)
                        return res;
                }
        } else if (res == 0) {
-               if (skinnydebug)
-                       ast_verbose("Skinny Client was lost, unregistering\n");
+               if (skinnydebug) {
+                       if (s->device) {
+                               ast_verbose("Skinny Client was lost, unregistering\n");
+                       } else {
+                               ast_verbose("Skinny Client failed to authenticate in %d seconds\n", auth_timeout);
+                       }
+               }
                skinny_unregister(NULL, s);
                return -1;
        }
@@ -4594,18 +4626,35 @@ static void *accept_thread(void *ignore)
                        ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
                        continue;
                }
+
+               if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= auth_limit) {
+                       close(as);
+                       ast_atomic_fetchadd_int(&unauth_sessions, -1);
+                       continue;
+               }
+
                p = getprotobyname("tcp");
                if(p) {
                        if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
                                ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
                        }
                }
-               if (!(s = ast_calloc(1, sizeof(struct skinnysession))))
+               if (!(s = ast_calloc(1, sizeof(struct skinnysession)))) {
+                       close(as);
+                       ast_atomic_fetchadd_int(&unauth_sessions, -1);
                        continue;
+               }
 
                memcpy(&s->sin, &sin, sizeof(sin));
                ast_mutex_init(&s->lock);
                s->fd = as;
+
+               if(time(&s->start) == -1) {
+                       ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
+                       destroy_session(s);
+                       continue;
+               }
+
                ast_mutex_lock(&sessionlock);
                s->next = sessions;
                sessions = s;
@@ -4756,6 +4805,24 @@ static int reload_config(void)
                        }
                } else if (!strcasecmp(v->name, "keepalive")) {
                        keep_alive = atoi(v->value);
+               } else if (!strcasecmp(v->name, "authtimeout")) {
+                       int timeout = atoi(v->value);
+
+                       if (timeout < 1) {
+                               ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", v->value);
+                               auth_timeout = DEFAULT_AUTH_TIMEOUT;
+                       } else {
+                               auth_timeout = timeout;
+                       }
+               } else if (!strcasecmp(v->name, "authlimit")) {
+                       int limit = atoi(v->value);
+
+                       if (limit < 1) {
+                               ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", v->value);
+                               auth_limit = DEFAULT_AUTH_LIMIT;
+                       } else {
+                               auth_limit = limit;
+                       }
                } else if (!strcasecmp(v->name, "dateformat")) {
                        memcpy(date_format, v->value, sizeof(date_format));
                } else if (!strcasecmp(v->name, "allow")) {
index f8a86f85a2a8111664144e88c18fab74445d7e07..c05fa9417cd1e192cf58f794b7e180069effe3d5 100644 (file)
@@ -26,7 +26,12 @@ bindport=8088
 ; requests must begin with /asterisk
 ;
 ;prefix=asterisk
-
+;
+; sessionlimit specifies the maximum number of httpsessions that will be
+; allowed to exist at any given time. (default: 100)
+;
+;sessionlimit=100
+;
 ; The post_mappings section maps URLs to real paths on the filesystem.  If a
 ; POST is done from within an authenticated manager session to one of the
 ; configured POST mappings, then any files in the POST will be placed in the
index 87c37e4d2f41760d743aa6bfd2d01e4590e7827c..07e6d5b4ebd3babff293f7f7cabc1b24f140d9c3 100644 (file)
@@ -9,6 +9,15 @@ dateformat=M-D-Y       ; M,D,Y in any order (6 chars max)
                        ; Use M for month, D for day, Y for year, A for 12-hour time.
 keepalive=120
 
+;authtimeout = 30       ; 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)
+
+;authlimit = 50         ; authlimit specifies the maximum number of
+                       ; unauthenticated sessions that will be allowed to
+                        ; connect at any given time. (default: 50)
+
 ;allow=all             ; see doc/rtp-packetization for framing options
 ;disallow=
 
index 213701d61fc0186c11d357614404dc0e9b5a8a0c..b5782899bbc7f9542b3801e9156cb09f14ddd8c5 100644 (file)
@@ -60,6 +60,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #define MAX_PREFIX 80
 #define DEFAULT_PREFIX "/asterisk"
+#define DEFAULT_SESSION_LIMIT 100
 
 struct ast_http_server_instance {
        FILE *f;
@@ -77,6 +78,8 @@ static char prefix[MAX_PREFIX];
 static int prefix_len;
 static struct sockaddr_in oldsin;
 static int enablestatic;
+static int session_limit = DEFAULT_SESSION_LIMIT;
+static int session_count = 0;
 
 /*! \brief Limit the kinds of files we're willing to serve up */
 static struct {
@@ -516,6 +519,7 @@ static void *ast_httpd_helper_thread(void *data)
        }
        fclose(ser->f);
        free(ser);
+       ast_atomic_fetchadd_int(&session_count, -1);
        return NULL;
 }
 
@@ -534,15 +538,23 @@ static void *http_root(void *data)
                ast_wait_for_input(httpfd, -1);
                sinlen = sizeof(sin);
                fd = accept(httpfd, (struct sockaddr *)&sin, &sinlen);
+
                if (fd < 0) {
                        if ((errno != EAGAIN) && (errno != EINTR))
                                ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
                        continue;
                }
+
+               if (ast_atomic_fetchadd_int(&session_count, +1) >= session_limit) {
+                       close(fd);
+                       continue;
+               }
+
                ser = ast_calloc(1, sizeof(*ser));
                if (!ser) {
                        ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
                        close(fd);
+                       ast_atomic_fetchadd_int(&session_count, -1);
                        continue;
                }
                flags = fcntl(fd, F_GETFL);
@@ -557,12 +569,14 @@ static void *http_root(void *data)
                                ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
                                fclose(ser->f);
                                free(ser);
+                               ast_atomic_fetchadd_int(&session_count, -1);
                        }
                        pthread_attr_destroy(&attr);
                } else {
                        ast_log(LOG_WARNING, "fdopen failed!\n");
                        close(ser->fd);
                        free(ser);
+                       ast_atomic_fetchadd_int(&session_count, -1);
                }
        }
        return NULL;
@@ -679,8 +693,17 @@ static int __ast_http_load(int reload)
                                } else {
                                        newprefix[0] = '\0';
                                }
-                                       
+                       } else if (!strcasecmp(v->name, "sessionlimit")) {
+                               int limit = atoi(v->value);
+
+                               if (limit < 1) {
+                                       ast_log(LOG_WARNING, "Invalid sessionlimit value '%s', using default value\n", v->value);
+                                       session_limit = DEFAULT_SESSION_LIMIT;
+                               } else {
+                                       session_limit = limit;
+                               }
                        }
+
                        v = v->next;
                }
                ast_config_destroy(cfg);
index 6d4ab0a8ac1194a6fbc7ce8f5c3bbc9532c19029..65a47e8a5acff2732acdbbea8a9a2e6dc95787d3 100644 (file)
@@ -2017,6 +2017,24 @@ static int action_originate(struct mansession *s, const struct message *m)
                format = 0;
                ast_parse_allow_disallow(NULL, &format, codecs, 1);
        }
+       if (!ast_strlen_zero(app)) {
+               /* To run the System application (or anything else that goes to
+                * shell), you must have the additional System privilege */
+               if (!(s->session->writeperm & EVENT_FLAG_SYSTEM)
+                       && (
+                               strcasestr(app, "system") == 0 || /* System(rm -rf /)
+                                                                    TrySystem(rm -rf /)       */
+                               strcasestr(app, "exec") ||        /* Exec(System(rm -rf /))
+                                                                    TryExec(System(rm -rf /)) */
+                               strcasestr(app, "agi") ||         /* AGI(/bin/rm,-rf /)
+                                                                    EAGI(/bin/rm,-rf /)       */
+                               strstr(appdata, "SHELL") ||       /* NoOp(${SHELL(rm -rf /)})  */
+                               strstr(appdata, "EVAL")           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
+                               )) {
+                       astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
+                       return 0;
+               }
+       }
        /* Allocate requested channel variables */
        vars = astman_get_variables(m);