From: Matthew Nicholson Date: Thu, 21 Apr 2011 18:19:21 +0000 (+0000) Subject: Added limits to the number of unauthenticated sessions TCP based protocols are allowe... X-Git-Tag: 1.4.42-rc1~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9bd88702b7ecd5fea524ed27aa407a0b10a0104c;p=thirdparty%2Fasterisk.git Added limits to the number of unauthenticated sessions TCP based protocols are allowed to have open simultaneously. Also added timeouts for unauthenticated sessions where it made sense to do so. 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 --- diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index 59e8cfa790..f6bce77d30 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -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")) { diff --git a/configs/http.conf.sample b/configs/http.conf.sample index f8a86f85a2..c05fa9417c 100644 --- a/configs/http.conf.sample +++ b/configs/http.conf.sample @@ -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 diff --git a/configs/skinny.conf.sample b/configs/skinny.conf.sample index 87c37e4d2f..07e6d5b4eb 100644 --- a/configs/skinny.conf.sample +++ b/configs/skinny.conf.sample @@ -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= diff --git a/main/http.c b/main/http.c index 213701d61f..b5782899bb 100644 --- a/main/http.c +++ b/main/http.c @@ -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); diff --git a/main/manager.c b/main/manager.c index 6d4ab0a8ac..65a47e8a5a 100644 --- a/main/manager.c +++ b/main/manager.c @@ -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);