summaryrefslogtreecommitdiff
path: root/main/manager.c
diff options
context:
space:
mode:
authorRussell Bryant <russell@russellbryant.com>2009-07-11 19:15:03 +0000
committerRussell Bryant <russell@russellbryant.com>2009-07-11 19:15:03 +0000
commit4cf8a968fd37e4ab72052bf5549072abda81e1be (patch)
tree083f8af40de7b9c624b37fcc20d48f4c7da8ce94 /main/manager.c
parentc01286976af9db85c775ab7ec003c33664c5e95f (diff)
Add an API for reporting security events, and a security event logging module.
This commit introduces the security events API. This API is to be used by Asterisk components to report events that have security implications. A simple example is when a connection is made but fails authentication. These events can be used by external tools manipulate firewall rules or something similar after detecting unusual activity based on security events. Inside of Asterisk, the events go through the ast_event API. This means that they have a binary encoding, and it is easy to write code to subscribe to these events and do something with them. One module is provided that is a subscriber to these events - res_security_log. This module turns security events into a parseable text format and sends them to the "security" logger level. Using logger.conf, these log entries may be sent to a file, or to syslog. One service, AMI, has been fully updated for reporting security events. AMI was chosen as it was a fairly straight forward service to convert. The next target will be chan_sip. That will be more complicated and will be done as its own project as the next phase of security events work. For more information on the security events framework, see the documentation generated from doc/tex/. "make asterisk.pdf" Review: https://reviewboard.asterisk.org/r/273/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@206021 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/manager.c')
-rw-r--r--main/manager.c267
1 files changed, 263 insertions, 4 deletions
diff --git a/main/manager.c b/main/manager.c
index 0670da36d..c7e4ce4c6 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -74,6 +74,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/term.h"
#include "asterisk/astobj2.h"
#include "asterisk/features.h"
+#include "asterisk/security_events.h"
/*** DOCUMENTATION
<manager name="Ping" language="en_US">
@@ -807,6 +808,7 @@ struct mansession_session {
pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */
time_t sessionstart; /*!< Session start time */
+ struct timeval sessionstart_tv; /*!< Session start time */
time_t sessiontimeout; /*!< Session timeout if HTTP */
char username[80]; /*!< Logged in username */
char challenge[10]; /*!< Authentication challenge */
@@ -834,6 +836,7 @@ struct mansession_session {
*/
struct mansession {
struct mansession_session *session;
+ struct ast_tcptls_session_instance *tcptls_session;
FILE *f;
int fd;
ast_mutex_t lock;
@@ -1735,6 +1738,241 @@ static int set_eventmask(struct mansession *s, const char *eventmask)
return maskint;
}
+static enum ast_security_event_transport_type mansession_get_transport(const struct mansession *s)
+{
+ return s->tcptls_session->parent->tls_cfg ? AST_SECURITY_EVENT_TRANSPORT_TLS :
+ AST_SECURITY_EVENT_TRANSPORT_TCP;
+}
+
+static struct sockaddr_in *mansession_encode_sin_local(const struct mansession *s,
+ struct sockaddr_in *sin_local)
+{
+ *sin_local = s->tcptls_session->parent->local_address;
+
+ return sin_local;
+}
+
+static void report_invalid_user(const struct mansession *s, const char *username)
+{
+ struct sockaddr_in sin_local;
+ char session_id[32];
+ struct ast_security_event_inval_acct_id inval_acct_id = {
+ .common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID,
+ .common.version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
+ .common.service = "AMI",
+ .common.account_id = username,
+ .common.session_tv = &s->session->sessionstart_tv,
+ .common.local_addr = {
+ .sin = mansession_encode_sin_local(s, &sin_local),
+ .transport = mansession_get_transport(s),
+ },
+ .common.remote_addr = {
+ .sin = &s->session->sin,
+ .transport = mansession_get_transport(s),
+ },
+ .common.session_id = session_id,
+ };
+
+ snprintf(session_id, sizeof(session_id), "%p", s);
+
+ ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
+}
+
+static void report_failed_acl(const struct mansession *s, const char *username)
+{
+ struct sockaddr_in sin_local;
+ char session_id[32];
+ struct ast_security_event_failed_acl failed_acl_event = {
+ .common.event_type = AST_SECURITY_EVENT_FAILED_ACL,
+ .common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
+ .common.service = "AMI",
+ .common.account_id = username,
+ .common.session_tv = &s->session->sessionstart_tv,
+ .common.local_addr = {
+ .sin = mansession_encode_sin_local(s, &sin_local),
+ .transport = mansession_get_transport(s),
+ },
+ .common.remote_addr = {
+ .sin = &s->session->sin,
+ .transport = mansession_get_transport(s),
+ },
+ .common.session_id = session_id,
+ };
+
+ snprintf(session_id, sizeof(session_id), "%p", s->session);
+
+ ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
+}
+
+static void report_inval_password(const struct mansession *s, const char *username)
+{
+ struct sockaddr_in sin_local;
+ char session_id[32];
+ struct ast_security_event_inval_password inval_password = {
+ .common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD,
+ .common.version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION,
+ .common.service = "AMI",
+ .common.account_id = username,
+ .common.session_tv = &s->session->sessionstart_tv,
+ .common.local_addr = {
+ .sin = mansession_encode_sin_local(s, &sin_local),
+ .transport = mansession_get_transport(s),
+ },
+ .common.remote_addr = {
+ .sin = &s->session->sin,
+ .transport = mansession_get_transport(s),
+ },
+ .common.session_id = session_id,
+ };
+
+ snprintf(session_id, sizeof(session_id), "%p", s->session);
+
+ ast_security_event_report(AST_SEC_EVT(&inval_password));
+}
+
+static void report_auth_success(const struct mansession *s)
+{
+ struct sockaddr_in sin_local;
+ char session_id[32];
+ struct ast_security_event_successful_auth successful_auth = {
+ .common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
+ .common.version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
+ .common.service = "AMI",
+ .common.account_id = s->session->username,
+ .common.session_tv = &s->session->sessionstart_tv,
+ .common.local_addr = {
+ .sin = mansession_encode_sin_local(s, &sin_local),
+ .transport = mansession_get_transport(s),
+ },
+ .common.remote_addr = {
+ .sin = &s->session->sin,
+ .transport = mansession_get_transport(s),
+ },
+ .common.session_id = session_id,
+ };
+
+ snprintf(session_id, sizeof(session_id), "%p", s->session);
+
+ ast_security_event_report(AST_SEC_EVT(&successful_auth));
+}
+
+static void report_req_not_allowed(const struct mansession *s, const char *action)
+{
+ struct sockaddr_in sin_local;
+ char session_id[32];
+ char request_type[64];
+ struct ast_security_event_req_not_allowed req_not_allowed = {
+ .common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED,
+ .common.version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION,
+ .common.service = "AMI",
+ .common.account_id = s->session->username,
+ .common.session_tv = &s->session->sessionstart_tv,
+ .common.local_addr = {
+ .sin = mansession_encode_sin_local(s, &sin_local),
+ .transport = mansession_get_transport(s),
+ },
+ .common.remote_addr = {
+ .sin = &s->session->sin,
+ .transport = mansession_get_transport(s),
+ },
+ .common.session_id = session_id,
+
+ .request_type = request_type,
+ };
+
+ snprintf(session_id, sizeof(session_id), "%p", s->session);
+ snprintf(request_type, sizeof(request_type), "Action: %s", action);
+
+ ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
+}
+
+static void report_req_bad_format(const struct mansession *s, const char *action)
+{
+ struct sockaddr_in sin_local;
+ char session_id[32];
+ char request_type[64];
+ struct ast_security_event_req_bad_format req_bad_format = {
+ .common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT,
+ .common.version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION,
+ .common.service = "AMI",
+ .common.account_id = s->session->username,
+ .common.session_tv = &s->session->sessionstart_tv,
+ .common.local_addr = {
+ .sin = mansession_encode_sin_local(s, &sin_local),
+ .transport = mansession_get_transport(s),
+ },
+ .common.remote_addr = {
+ .sin = &s->session->sin,
+ .transport = mansession_get_transport(s),
+ },
+ .common.session_id = session_id,
+
+ .request_type = request_type,
+ };
+
+ snprintf(session_id, sizeof(session_id), "%p", s->session);
+ snprintf(request_type, sizeof(request_type), "Action: %s", action);
+
+ ast_security_event_report(AST_SEC_EVT(&req_bad_format));
+}
+
+static void report_failed_challenge_response(const struct mansession *s,
+ const char *response, const char *expected_response)
+{
+ struct sockaddr_in sin_local;
+ char session_id[32];
+ struct ast_security_event_chal_resp_failed chal_resp_failed = {
+ .common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED,
+ .common.version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
+ .common.service = "AMI",
+ .common.account_id = s->session->username,
+ .common.session_tv = &s->session->sessionstart_tv,
+ .common.local_addr = {
+ .sin = mansession_encode_sin_local(s, &sin_local),
+ .transport = mansession_get_transport(s),
+ },
+ .common.remote_addr = {
+ .sin = &s->session->sin,
+ .transport = mansession_get_transport(s),
+ },
+ .common.session_id = session_id,
+
+ .challenge = s->session->challenge,
+ .response = response,
+ .expected_response = expected_response,
+ };
+
+ snprintf(session_id, sizeof(session_id), "%p", s->session);
+
+ ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
+}
+
+static void report_session_limit(const struct mansession *s)
+{
+ struct sockaddr_in sin_local;
+ char session_id[32];
+ struct ast_security_event_session_limit session_limit = {
+ .common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT,
+ .common.version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION,
+ .common.service = "AMI",
+ .common.account_id = s->session->username,
+ .common.session_tv = &s->session->sessionstart_tv,
+ .common.local_addr = {
+ .sin = mansession_encode_sin_local(s, &sin_local),
+ .transport = mansession_get_transport(s),
+ },
+ .common.remote_addr = {
+ .sin = &s->session->sin,
+ .transport = mansession_get_transport(s),
+ },
+ .common.session_id = session_id,
+ };
+
+ snprintf(session_id, sizeof(session_id), "%p", s->session);
+
+ ast_security_event_report(AST_SEC_EVT(&session_limit));
+}
+
/*
* Here we start with action_ handlers for AMI actions,
* and the internal functions used by them.
@@ -1757,8 +1995,10 @@ static int authenticate(struct mansession *s, const struct message *m)
AST_RWLIST_WRLOCK(&users);
if (!(user = get_manager_by_name_locked(username))) {
+ report_invalid_user(s, username);
ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
} else if (user->ha && !ast_apply_ha(user->ha, &(s->session->sin))) {
+ report_failed_acl(s, username);
ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
} else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
const char *key = astman_get_header(m, "Key");
@@ -1777,13 +2017,19 @@ static int authenticate(struct mansession *s, const struct message *m)
len += sprintf(md5key + len, "%2.2x", digest[x]);
if (!strcmp(md5key, key)) {
error = 0;
+ } else {
+ report_failed_challenge_response(s, key, md5key);
}
} else {
ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
S_OR(s->session->challenge, ""));
}
- } else if (password && user->secret && !strcmp(password, user->secret)) {
- error = 0;
+ } else if (user->secret) {
+ if (!strcmp(password, user->secret)) {
+ error = 0;
+ } else {
+ report_inval_password(s, username);
+ }
}
if (error) {
@@ -1799,8 +2045,11 @@ static int authenticate(struct mansession *s, const struct message *m)
s->session->writeperm = user->writeperm;
s->session->writetimeout = user->writetimeout;
s->session->sessionstart = time(NULL);
+ s->session->sessionstart_tv = ast_tvnow();
set_eventmask(s, astman_get_header(m, "Events"));
+ report_auth_success(s);
+
AST_RWLIST_UNLOCK(&users);
return 0;
}
@@ -3550,6 +3799,7 @@ static int process_message(struct mansession *s, const struct message *m)
ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
if (ast_strlen_zero(action)) {
+ report_req_bad_format(s, "NONE");
mansession_lock(s);
astman_send_error(s, m, "Missing action in request");
mansession_unlock(s);
@@ -3557,6 +3807,9 @@ static int process_message(struct mansession *s, const struct message *m)
}
if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
+ if (!s->session->authenticated) {
+ report_req_not_allowed(s, action);
+ }
mansession_lock(s);
astman_send_error(s, m, "Permission denied");
mansession_unlock(s);
@@ -3566,6 +3819,7 @@ static int process_message(struct mansession *s, const struct message *m)
if (!allowmultiplelogin && !s->session->authenticated && user &&
(!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
if (check_manager_session_inuse(user)) {
+ report_session_limit(s);
sleep(1);
mansession_lock(s);
astman_send_error(s, m, "Login Already In Use");
@@ -3583,7 +3837,7 @@ static int process_message(struct mansession *s, const struct message *m)
call_func = tmp->func;
} else {
astman_send_error(s, m, "Permission denied");
- tmp = NULL;
+ report_req_not_allowed(s, action);
}
break;
}
@@ -3595,6 +3849,9 @@ static int process_message(struct mansession *s, const struct message *m)
ret = call_func(s, m);
} else {
char buf[512];
+ if (!tmp) {
+ report_req_bad_format(s, action);
+ }
snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
mansession_lock(s);
astman_send_error(s, m, buf);
@@ -3733,7 +3990,9 @@ static void *session_do(void *data)
{
struct ast_tcptls_session_instance *ser = data;
struct mansession_session *session = build_mansession(ser->remote_address);
- struct mansession s = { NULL, };
+ struct mansession s = {
+ .tcptls_session = data,
+ };
int flags;
int res;