Merged revisions 312761 via svnmerge from

https://origsvn.digium.com/svn/asterisk/branches/1.4

........
  r312761 | mnicholson | 2011-04-05 09:10:34 -0500 (Tue, 05 Apr 2011) | 8 lines
  
  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.6.2@312764 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.6.2
Matthew Nicholson 14 years ago
parent f24755ffd7
commit 1c272a5d15

@ -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

@ -127,6 +127,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 displayconnects;
static int allowmultiplelogin = 1;
@ -135,9 +137,12 @@ static int httptimeout;
static int broken_events_action;
static int manager_enabled = 0;
static int webmanager_enabled = 0;
static int authtimeout;
static int 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 */
@ -214,6 +219,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;
@ -1792,6 +1798,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));
@ -3130,6 +3137,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
@ -3157,6 +3166,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) {
@ -3167,7 +3190,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;
@ -3200,6 +3223,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 */
@ -3207,6 +3231,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))
@ -3230,14 +3265,23 @@ 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;
struct protoent *p;
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;
}
/* here we set TCP_NODELAY on the socket to disable Nagle's algorithm.
* This is necessary to prevent delays (caused by buffering) as we
@ -3280,6 +3324,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)
@ -3291,6 +3342,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));
@ -4186,6 +4238,8 @@ static int __init_manager(int reload)
block_sockets = DEFAULT_BLOCKSOCKETS;
timestampevents = DEFAULT_TIMESTAMPEVENTS;
httptimeout = DEFAULT_HTTPTIMEOUT;
authtimeout = DEFAULT_AUTHTIMEOUT;
authlimit = DEFAULT_AUTHLIMIT;
if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf, or configuration is invalid. Asterisk management interface (AMI) disabled.\n");
@ -4250,6 +4304,22 @@ 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);
} 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);
} else {
authlimit = limit;
}
} else {
ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
var->name, val);

Loading…
Cancel
Save