Merged revisions 314607 via svnmerge from

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

........
  r314607 | mnicholson | 2011-04-21 13:19:21 -0500 (Thu, 21 Apr 2011) | 14 lines
  
  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.6.2@314620 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.6.2
Matthew Nicholson 14 years ago
parent dd044c85d7
commit 2939c487cd

@ -500,6 +500,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define DEFAULT_MWI_EXPIRY 3600
#define DEFAULT_REGISTRATION_TIMEOUT 20
#define DEFAULT_MAX_FORWARDS "70"
#define DEFAULT_AUTHLIMIT 100
#define DEFAULT_AUTHTIMEOUT 30
/* guard limit must be larger than guard secs */
/* guard min must be < 1000, and should be >= 250 */
@ -552,6 +554,10 @@ static int mwi_expiry = DEFAULT_MWI_EXPIRY;
#define SDP_MAX_RTPMAP_CODECS 32 /*!< Maximum number of codecs allowed in received SDP */
static int unauth_sessions = 0;
static int authlimit = DEFAULT_AUTHLIMIT;
static int authtimeout = DEFAULT_AUTHTIMEOUT;
/*! \brief Global jitterbuffer configuration - by default, jb is disabled */
static struct ast_jb_conf default_jbconf =
{
@ -1205,6 +1211,7 @@ struct sip_request {
char debug; /*!< print extra debugging if non zero */
char has_to_tag; /*!< non-zero if packet has To: tag */
char ignore; /*!< if non-zero This is a re-transmit, ignore it */
char authenticated; /*!< non-zero if this request was authenticated */
/* Array of offsets into the request string of each SIP header*/
ptrdiff_t header[SIP_MAX_HEADERS];
/* Array of offsets into the request string of each SDP line*/
@ -2913,21 +2920,48 @@ static void *sip_tcp_worker_fn(void *data)
return _sip_tcp_helper_thread(NULL, tcptls_session);
}
/*! \brief Check if the authtimeout has expired.
* \param start the time when the session started
*
* \retval 0 the timeout has expired
* \retval -1 error
* \return the number of milliseconds until the timeout will expire
*/
static int sip_check_authtimeout(time_t start)
{
int timeout;
time_t now;
if(time(&now) == -1) {
ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
return -1;
}
timeout = (authtimeout - (now - start)) * 1000;
if (timeout < 0) {
/* we have timed out */
return 0;
}
return timeout;
}
/*! \brief SIP TCP thread management function
This function reads from the socket, parses the packet into a request
*/
static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session)
{
int res, cl;
int res, cl, timeout = -1, authenticated = 0, flags;
time_t start;
struct sip_request req = { 0, } , reqcpy = { 0, };
struct sip_threadinfo *me = NULL;
char buf[1024] = "";
struct pollfd fds[2] = { { 0 }, { 0 }, };
struct ast_tcptls_session_args *ca = NULL;
/* If this is a server session, then the connection has already been setup,
* simply create the threadinfo object so we can access this thread for writing.
*
/* If this is a server session, then the connection has already been
* setup. Check if the authlimit has been reached and if not create the
* threadinfo object so we can access this thread for writing.
*
* if this is a client connection more work must be done.
* 1. We own the parent session args for a client connection. This pointer needs
* to be held on to so we can decrement it's ref count on thread destruction.
@ -2936,6 +2970,22 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
* 3. Last, the tcptls_session must be started.
*/
if (!tcptls_session->client) {
if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= authlimit) {
/* unauth_sessions is decremented in the cleanup code */
goto cleanup;
}
if ((flags = fcntl(tcptls_session->fd, F_GETFL)) == -1) {
ast_log(LOG_ERROR, "error setting socket to non blocking mode, fcntl() failed: %s\n", strerror(errno));
goto cleanup;
}
flags |= O_NONBLOCK;
if (fcntl(tcptls_session->fd, F_SETFL, flags) == -1) {
ast_log(LOG_ERROR, "error setting socket to non blocking mode, fcntl() failed: %s\n", strerror(errno));
goto cleanup;
}
if (!(me = sip_threadinfo_create(tcptls_session, tcptls_session->ssl ? SIP_TRANSPORT_TLS : SIP_TRANSPORT_TCP))) {
goto cleanup;
}
@ -2965,14 +3015,41 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
if (!(reqcpy.data = ast_str_create(SIP_MIN_PACKET)))
goto cleanup;
if(time(&start) == -1) {
ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
goto cleanup;
}
for (;;) {
struct ast_str *str_save;
res = ast_poll(fds, 2, -1); /* polls for both socket and alert_pipe */
if (!tcptls_session->client && req.authenticated && !authenticated) {
authenticated = 1;
ast_atomic_fetchadd_int(&unauth_sessions, -1);
}
/* calculate the timeout for unauthenticated server sessions */
if (!tcptls_session->client && !authenticated ) {
if ((timeout = sip_check_authtimeout(start)) < 0) {
goto cleanup;
}
if (timeout == 0) {
ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
goto cleanup;
}
} else {
timeout = -1;
}
res = ast_poll(fds, 2, timeout); /* polls for both socket and alert_pipe */
if (res < 0) {
ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res);
goto cleanup;
} else if (res == 0) {
/* timeout */
ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
goto cleanup;
}
/* handle the socket event, check for both reads from the socket fd,
@ -3005,6 +3082,29 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
/* Read in headers one line at a time */
while (req.len < 4 || strncmp(REQ_OFFSET_TO_STR(&req, len - 4), "\r\n\r\n", 4)) {
if (!tcptls_session->client && !authenticated ) {
if ((timeout = sip_check_authtimeout(start)) < 0) {
goto cleanup;
}
if (timeout == 0) {
ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
goto cleanup;
}
} else {
timeout = -1;
}
res = ast_wait_for_input(tcptls_session->fd, timeout);
if (res < 0) {
ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res);
goto cleanup;
} else if (res == 0) {
/* timeout */
ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
goto cleanup;
}
ast_mutex_lock(&tcptls_session->lock);
if (!fgets(buf, sizeof(buf), tcptls_session->f)) {
ast_mutex_unlock(&tcptls_session->lock);
@ -3022,6 +3122,29 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
if (sscanf(get_header(&reqcpy, "Content-Length"), "%30d", &cl)) {
while (cl > 0) {
size_t bytes_read;
if (!tcptls_session->client && !authenticated ) {
if ((timeout = sip_check_authtimeout(start)) < 0) {
goto cleanup;
}
if (timeout == 0) {
ast_debug(2, "SIP %s server timed out", tcptls_session->ssl ? "SSL": "TCP");
goto cleanup;
}
} else {
timeout = -1;
}
res = ast_wait_for_input(tcptls_session->fd, timeout);
if (res < 0) {
ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res);
goto cleanup;
} else if (res == 0) {
/* timeout */
ast_debug(2, "SIP %s server timed out", tcptls_session->ssl ? "SSL": "TCP");
goto cleanup;
}
ast_mutex_lock(&tcptls_session->lock);
if (!(bytes_read = fread(buf, 1, MIN(sizeof(buf) - 1, cl), tcptls_session->f))) {
ast_mutex_unlock(&tcptls_session->lock);
@ -3080,6 +3203,10 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
ast_debug(2, "Shutting down thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP");
cleanup:
if (!tcptls_session->client && !authenticated) {
ast_atomic_fetchadd_int(&unauth_sessions, -1);
}
if (me) {
ao2_t_unlink(threadt, me, "Removing tcptls helper thread, thread is closing");
ao2_t_ref(me, -1, "Removing tcp_helper_threads threadinfo ref");
@ -20474,6 +20601,8 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
}
}
req->authenticated = 1;
/* We have a succesful authentication, process the SDP portion if there is one */
if (find_sdp(req)) {
if (process_sdp(p, req, SDP_T38_INITIATE)) {
@ -22047,8 +22176,10 @@ static int handle_request_register(struct sip_pvt *p, struct sip_request *req, s
get_header(req, "To"), ast_inet_ntoa(sin->sin_addr),
reason);
append_history(p, "RegRequest", "Failed : Account %s : %s", get_header(req, "To"), reason);
} else
} else {
req->authenticated = 1;
append_history(p, "RegRequest", "Succeeded : Account %s", get_header(req, "To"));
}
if (res < 1) {
/* Destroy the session, but keep us around for just a bit in case they don't
@ -22503,6 +22634,11 @@ static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin)
}
p->recv = *sin;
/* if we have an owner, then this request has been authenticated */
if (p->owner) {
req->authenticated = 1;
}
if (p->do_history) /* This is a request or response, note what it was for */
append_history(p, "Rx", "%s / %s / %s", req->data->str, get_header(req, "CSeq"), REQ_OFFSET_TO_STR(req, rlPart2));
@ -25064,6 +25200,8 @@ static int reload_config(enum channelreloadreason reason)
global_qualifyfreq = DEFAULT_QUALIFYFREQ;
global_t38_maxdatagram = -1;
global_shrinkcallerid = 1;
authlimit = DEFAULT_AUTHLIMIT;
authtimeout = DEFAULT_AUTHTIMEOUT;
sip_cfg.matchexterniplocally = DEFAULT_MATCHEXTERNIPLOCALLY;
@ -25310,6 +25448,18 @@ static int reload_config(enum channelreloadreason reason)
mwi_expiry = atoi(v->value);
if (mwi_expiry < 1)
mwi_expiry = DEFAULT_MWI_EXPIRY;
} else if (!strcasecmp(v->name, "tcpauthtimeout")) {
if (ast_parse_arg(v->value, PARSE_INT32|PARSE_DEFAULT|PARSE_IN_RANGE,
&authtimeout, DEFAULT_AUTHTIMEOUT, 1, INT_MAX)) {
ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of %s\n",
v->name, v->value, v->lineno, config);
}
} else if (!strcasecmp(v->name, "tcpauthlimit")) {
if (ast_parse_arg(v->value, PARSE_INT32|PARSE_DEFAULT|PARSE_IN_RANGE,
&authlimit, DEFAULT_AUTHLIMIT, 1, INT_MAX)) {
ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of %s\n",
v->name, v->value, v->lineno, config);
}
} else if (!strcasecmp(v->name, "sipdebug")) {
if (ast_true(v->value))
sipdebug |= sip_debug_config;

@ -100,6 +100,8 @@ 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 struct {
unsigned int tos;
@ -111,6 +113,9 @@ static struct {
} qos = { 0, 0, 0, 0, 0, 0 };
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 global_vmexten[AST_MAX_EXTENSION]; /* Voicemail pilot number */
static char used_context[AST_MAX_EXTENSION]; /* placeholder to check if context are already used in regcontext */
static char regcontext[AST_MAX_CONTEXT]; /* Context for auto-extension */
@ -1307,6 +1312,7 @@ static struct ast_jb_conf global_jbconf;*/
struct skinnysession {
pthread_t t;
ast_mutex_t lock;
time_t start;
struct sockaddr_in sin;
int fd;
char inbuf[SKINNY_MAX_PACKET];
@ -4558,6 +4564,8 @@ static int handle_register_message(struct skinny_req *req, struct skinnysession
return 0;
}
ast_atomic_fetchadd_int(&unauth_sessions, -1);
ast_verb(3, "Device '%s' successfully registered\n", name);
d = s->device;
@ -6244,6 +6252,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);
ast_free(s);
@ -6259,13 +6270,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_verb(1, "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) {
@ -6274,8 +6302,13 @@ static int get_input(struct skinnysession *s)
return res;
}
} else if (res == 0) {
if (skinnydebug)
ast_verb(1, "Skinny Client was lost, unregistering\n");
if (skinnydebug) {
if (s->device) {
ast_verb(1, "Skinny Client was lost, unregistering\n");
} else {
ast_verb(1, "Skinny Client failed to authenticate in %d seconds\n", auth_timeout);
}
}
skinny_unregister(NULL, s);
return -1;
}
@ -6408,18 +6441,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_LIST_LOCK(&sessions);
AST_LIST_INSERT_HEAD(&sessions, s, list);
AST_LIST_UNLOCK(&sessions);
@ -6573,6 +6623,26 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
} else if (!strcasecmp(v->name, "keepalive")) {
keep_alive = atoi(v->value);
continue;
} 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;
}
continue;
} 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;
}
continue;
} else if (!strcasecmp(v->name, "regcontext")) {
ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
stringp = newcontexts;

@ -32,6 +32,11 @@ bindaddr=127.0.0.1
;
;prefix=asterisk
;
; sessionlimit specifies the maximum number of httpsessions that will be
; allowed to exist at any given time. (default: 100)
;
;sessionlimit=100
;
; Whether Asterisk should serve static content from http-static
; Default is no.
;

@ -144,6 +144,16 @@ tcpbindaddr=0.0.0.0 ; IP address for TCP server to bind to (0.0.0.0
; A list of valid SSL cipher strings can be found at:
; http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
;tcpauthtimeout = 30 ; tcpauthtimeout 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)
;tcpauthlimit = 100 ; tcpauthlimit specifies the maximum number of
; unauthenticated sessions that will be allowed
; to connect at any given time. (default: 100)
srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; Note: Asterisk only uses the first host
; in SRV records

@ -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)
;vmexten=8500 ; Systemwide voicemailmain pilot number
; It must be in the same context as the calling
; device/line

@ -53,12 +53,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#define MAX_PREFIX 80
#define DEFAULT_SESSION_LIMIT 100
/* See http.h for more information about the SSL implementation */
#if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
#define DO_SSL /* comment in/out if you want to support ssl */
#endif
static int session_limit = DEFAULT_SESSION_LIMIT;
static int session_count = 0;
static struct ast_tls_config http_tls_cfg;
static void *httpd_helper_thread(void *arg);
@ -677,6 +681,10 @@ static void *httpd_helper_thread(void *data)
unsigned int static_content = 0;
struct ast_variable *tail = headers;
if (ast_atomic_fetchadd_int(&session_count, +1) >= session_limit) {
goto done;
}
if (!fgets(buf, sizeof(buf), ser->f)) {
goto done;
}
@ -787,6 +795,7 @@ static void *httpd_helper_thread(void *data)
}
done:
ast_atomic_fetchadd_int(&session_count, -1);
fclose(ser->f);
ao2_ref(ser, -1);
ser = NULL;
@ -936,6 +945,12 @@ static int __ast_http_load(int reload)
}
} else if (!strcasecmp(v->name, "redirect")) {
add_redirect(v->value);
} else if (!strcasecmp(v->name, "sessionlimit")) {
if (ast_parse_arg(v->value, PARSE_INT32|PARSE_DEFAULT|PARSE_IN_RANGE,
&session_limit, DEFAULT_SESSION_LIMIT, 1, INT_MAX)) {
ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
v->name, v->value, v->lineno);
}
} else {
ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
}

@ -2515,6 +2515,25 @@ 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") || /* 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);
@ -2548,21 +2567,6 @@ static int action_originate(struct mansession *s, const struct message *m)
}
}
} else 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") || /* 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;
}
res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
} else {
if (exten && context && pi)

Loading…
Cancel
Save