From: Matthew Nicholson Date: Tue, 5 Apr 2011 14:10:34 +0000 (+0000) Subject: Limit the number of unauthenticated manager sessions and also limit the time they... X-Git-Tag: 1.4.42-rc1~28 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=059e1a2decdc0365b731fb5ce4124f08168e4159;p=thirdparty%2Fasterisk.git Limit the number of unauthenticated manager sessions and also limit the time they have to authenticate. AST-2011-005 (closes issue #18996) Reported by: tzafrir Tested by: mnicholson git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@312761 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- diff --git a/configs/manager.conf.sample b/configs/manager.conf.sample index 176af1a829..6637e963ec 100644 --- a/configs/manager.conf.sample +++ b/configs/manager.conf.sample @@ -26,6 +26,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 diff --git a/main/manager.c b/main/manager.c index 6d0bfae517..6d4ab0a8ac 100644 --- a/main/manager.c +++ b/main/manager.c @@ -104,6 +104,8 @@ static const int DEFAULT_DISPLAYCONNECTS = 1; /*!< Default setting for displayin static const int DEFAULT_TIMESTAMPEVENTS = 0; /*!< Default setting for timestampevents */ static const int DEFAULT_HTTPTIMEOUT = 60; /*!< Default manager http timeout */ static const int DEFAULT_BROKENEVENTSACTION = 0; /*!< Default setting for brokeneventsaction */ +static const int DEFAULT_AUTHTIMEOUT = 30; /*!< Default setting for authtimeout */ +static const int DEFAULT_AUTHLIMIT = 50; /*!< Default setting for authlimit */ static int enabled; @@ -113,10 +115,13 @@ static int displayconnects; static int timestampevents; static int httptimeout; static int broken_events_action; +static int authtimeout; +static int authlimit; static pthread_t t; static int block_sockets; static int num_sessions; +static int unauth_sessions = 0; /* Protected by the sessions list lock */ struct eventqent *master_eventq = NULL; @@ -222,6 +227,7 @@ struct mansession_session { struct eventqent *eventq; /* Timeout for ast_carefulwrite() */ int writetimeout; + time_t authstart; int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */ AST_LIST_ENTRY(mansession_session) list; }; @@ -2306,6 +2312,7 @@ static int process_message(struct mansession *s, const struct message *m) return -1; } else { s->session->authenticated = 1; + ast_atomic_fetchadd_int(&unauth_sessions, -1); if (option_verbose > 1) { if (displayconnects) { ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", @@ -2360,6 +2367,8 @@ static int get_input(struct mansession_session *s, char *output) int res; int x; struct pollfd fds[1]; + int timeout = -1; + time_t now; for (x = 1; x < s->inlen; x++) { if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) { /* Copy output data up to and including \r\n */ @@ -2378,7 +2387,22 @@ static int get_input(struct mansession_session *s, char *output) } fds[0].fd = s->fd; fds[0].events = POLLIN; + do { + /* calculate a timeout if we are not authenticated */ + if (!s->authenticated) { + if(time(&now) == -1) { + ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno)); + return -1; + } + + timeout = (authtimeout - (now - s->authstart)) * 1000; + if (timeout < 0) { + /* we have timed out */ + return 0; + } + } + ast_mutex_lock(&s->__lock); if (s->pending_event) { s->pending_event = 0; @@ -2388,7 +2412,7 @@ static int get_input(struct mansession_session *s, char *output) s->waiting_thread = pthread_self(); ast_mutex_unlock(&s->__lock); - res = ast_poll(fds, 1, -1); + res = ast_poll(fds, 1, timeout); ast_mutex_lock(&s->__lock); s->waiting_thread = AST_PTHREADT_NULL; @@ -2406,6 +2430,9 @@ static int get_input(struct mansession_session *s, char *output) if (res < 1) return -1; break; + } else { + /* timeout */ + return 0; } } while(1); s->inlen += res; @@ -2418,6 +2445,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 */ @@ -2427,6 +2455,17 @@ static int do_message(struct mansession *s) } res = get_input(s->session, 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) { /* Strip trailing \r\n */ @@ -2461,6 +2500,7 @@ static void *session_do(void *data) } 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 (option_verbose > 1) { if (displayconnects) ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr)); @@ -2534,14 +2574,25 @@ 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) >= authlimit) { + close(as); + ast_atomic_fetchadd_int(&unauth_sessions, -1); + ast_log(LOG_WARNING, "manager connection rejected, too many unauthenticated sessions.\n"); + 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 manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno)); } } - if (!(s = ast_calloc(1, sizeof(*s)))) + if (!(s = ast_calloc(1, sizeof(*s)))) { + close(as); + ast_atomic_fetchadd_int(&unauth_sessions, -1); continue; + } memcpy(&s->sin, &sin, sizeof(sin)); s->writetimeout = 100; @@ -2568,8 +2619,16 @@ static void *accept_thread(void *ignore) s->eventq = s->eventq->next; ast_atomic_fetchadd_int(&s->eventq->usecount, 1); AST_LIST_UNLOCK(&sessions); - if (ast_pthread_create_background(&t, &attr, session_do, s)) + if(time(&s->authstart) == -1) { + ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno)); + ast_atomic_fetchadd_int(&unauth_sessions, -1); destroy_session(s); + continue; + } + if (ast_pthread_create_background(&t, &attr, session_do, s)) { + ast_atomic_fetchadd_int(&unauth_sessions, -1); + destroy_session(s); + } } pthread_attr_destroy(&attr); return NULL; @@ -3106,6 +3165,8 @@ int init_manager(void) block_sockets = DEFAULT_BLOCKSOCKETS; timestampevents = DEFAULT_TIMESTAMPEVENTS; httptimeout = DEFAULT_HTTPTIMEOUT; + authtimeout = DEFAULT_AUTHTIMEOUT; + authlimit = DEFAULT_AUTHLIMIT; cfg = ast_config_load("manager.conf"); if (!cfg) { @@ -3143,6 +3204,26 @@ int init_manager(void) if ((val = ast_variable_retrieve(cfg, "general", "httptimeout"))) newhttptimeout = atoi(val); + if ((val = ast_variable_retrieve(cfg, "general", "authtimeout"))) { + int timeout = atoi(val); + + if (timeout < 1) { + ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", val); + } else { + authtimeout = timeout; + } + } + + if ((val = ast_variable_retrieve(cfg, "general", "authlimit"))) { + int limit = atoi(val); + + if (limit < 1) { + ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", val); + } else { + authlimit = limit; + } + } + memset(&ba, 0, sizeof(ba)); ba.sin_family = AF_INET; ba.sin_port = htons(portno);