summaryrefslogtreecommitdiff
path: root/main
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
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')
-rw-r--r--main/event.c156
-rw-r--r--main/manager.c267
-rw-r--r--main/security_events.c647
3 files changed, 992 insertions, 78 deletions
diff --git a/main/event.c b/main/event.c
index 6a70a6741..44c65714d 100644
--- a/main/event.c
+++ b/main/event.c
@@ -183,63 +183,77 @@ static struct {
};
/*!
- * The index of each entry _must_ match the event type number!
+ * \brief Event Names
*/
-static struct event_name {
- enum ast_event_type type;
- const char *name;
-} event_names[] = {
- { 0, "" },
- { AST_EVENT_CUSTOM, "Custom" },
- { AST_EVENT_MWI, "MWI" },
- { AST_EVENT_SUB, "Subscription" },
- { AST_EVENT_UNSUB, "Unsubscription" },
- { AST_EVENT_DEVICE_STATE, "DeviceState" },
- { AST_EVENT_DEVICE_STATE_CHANGE, "DeviceStateChange" },
- { AST_EVENT_CEL, "CEL" },
+static const char * const event_names[AST_EVENT_TOTAL] = {
+ [AST_EVENT_CUSTOM] = "Custom",
+ [AST_EVENT_MWI] = "MWI",
+ [AST_EVENT_SUB] = "Subscription",
+ [AST_EVENT_UNSUB] = "Unsubscription",
+ [AST_EVENT_DEVICE_STATE] = "DeviceState",
+ [AST_EVENT_DEVICE_STATE_CHANGE] = "DeviceStateChange",
+ [AST_EVENT_CEL] = "CEL",
+ [AST_EVENT_SECURITY] = "Security",
};
/*!
- * The index of each entry _must_ match the event ie number!
+ * \brief IE payload types and names
*/
-static struct ie_map {
- enum ast_event_ie_type ie_type;
+static const struct ie_map {
enum ast_event_ie_pltype ie_pltype;
const char *name;
-} ie_maps[] = {
- { 0, 0, "" },
- { AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, "NewMessages" },
- { AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, "OldMessages" },
- { AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, "Mailbox" },
- { AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, "UniqueID" },
- { AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, "EventType" },
- { AST_EVENT_IE_EXISTS, AST_EVENT_IE_PLTYPE_UINT, "Exists" },
- { AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "Device" },
- { AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, "State" },
- { AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "Context" },
- { AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, "EntityID" },
- { AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, "CELEventType" },
- { AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, "CELEventTime" },
- { AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, "CELEventTimeUSec" },
- { AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_UINT, "CELUserEventName" },
- { AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, "CELCIDName" },
- { AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, "CELCIDNum" },
- { AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, "CELExten" },
- { AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "CELContext" },
- { AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, "CELChanName" },
- { AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, "CELAppName" },
- { AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, "CELAppData" },
- { AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_STR, "CELAMAFlags" },
- { AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_UINT, "CELAcctCode" },
- { AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, "CELUniqueID" },
- { AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, "CELUserField" },
- { AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, "CELCIDani" },
- { AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, "CELCIDrdnis" },
- { AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, "CELCIDdnid" },
- { AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, "CELPeer" },
- { AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, "CELLinkedID" },
- { AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, "CELPeerAcct" },
- { AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, "CELExtra" },
+} ie_maps[AST_EVENT_IE_TOTAL] = {
+ [AST_EVENT_IE_NEWMSGS] = { AST_EVENT_IE_PLTYPE_UINT, "NewMessages" },
+ [AST_EVENT_IE_OLDMSGS] = { AST_EVENT_IE_PLTYPE_UINT, "OldMessages" },
+ [AST_EVENT_IE_MAILBOX] = { AST_EVENT_IE_PLTYPE_STR, "Mailbox" },
+ [AST_EVENT_IE_UNIQUEID] = { AST_EVENT_IE_PLTYPE_UINT, "UniqueID" },
+ [AST_EVENT_IE_EVENTTYPE] = { AST_EVENT_IE_PLTYPE_UINT, "EventType" },
+ [AST_EVENT_IE_EXISTS] = { AST_EVENT_IE_PLTYPE_UINT, "Exists" },
+ [AST_EVENT_IE_DEVICE] = { AST_EVENT_IE_PLTYPE_STR, "Device" },
+ [AST_EVENT_IE_STATE] = { AST_EVENT_IE_PLTYPE_UINT, "State" },
+ [AST_EVENT_IE_CONTEXT] = { AST_EVENT_IE_PLTYPE_STR, "Context" },
+ [AST_EVENT_IE_EID] = { AST_EVENT_IE_PLTYPE_RAW, "EntityID" },
+ [AST_EVENT_IE_CEL_EVENT_TYPE] = { AST_EVENT_IE_PLTYPE_UINT, "CELEventType" },
+ [AST_EVENT_IE_CEL_EVENT_TIME] = { AST_EVENT_IE_PLTYPE_UINT, "CELEventTime" },
+ [AST_EVENT_IE_CEL_EVENT_TIME_USEC] = { AST_EVENT_IE_PLTYPE_UINT, "CELEventTimeUSec" },
+ [AST_EVENT_IE_CEL_USEREVENT_NAME] = { AST_EVENT_IE_PLTYPE_UINT, "CELUserEventName" },
+ [AST_EVENT_IE_CEL_CIDNAME] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDName" },
+ [AST_EVENT_IE_CEL_CIDNUM] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDNum" },
+ [AST_EVENT_IE_CEL_EXTEN] = { AST_EVENT_IE_PLTYPE_STR, "CELExten" },
+ [AST_EVENT_IE_CEL_CONTEXT] = { AST_EVENT_IE_PLTYPE_STR, "CELContext" },
+ [AST_EVENT_IE_CEL_CHANNAME] = { AST_EVENT_IE_PLTYPE_STR, "CELChanName" },
+ [AST_EVENT_IE_CEL_APPNAME] = { AST_EVENT_IE_PLTYPE_STR, "CELAppName" },
+ [AST_EVENT_IE_CEL_APPDATA] = { AST_EVENT_IE_PLTYPE_STR, "CELAppData" },
+ [AST_EVENT_IE_CEL_AMAFLAGS] = { AST_EVENT_IE_PLTYPE_STR, "CELAMAFlags" },
+ [AST_EVENT_IE_CEL_ACCTCODE] = { AST_EVENT_IE_PLTYPE_UINT, "CELAcctCode" },
+ [AST_EVENT_IE_CEL_UNIQUEID] = { AST_EVENT_IE_PLTYPE_STR, "CELUniqueID" },
+ [AST_EVENT_IE_CEL_USERFIELD] = { AST_EVENT_IE_PLTYPE_STR, "CELUserField" },
+ [AST_EVENT_IE_CEL_CIDANI] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDani" },
+ [AST_EVENT_IE_CEL_CIDRDNIS] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDrdnis" },
+ [AST_EVENT_IE_CEL_CIDDNID] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDdnid" },
+ [AST_EVENT_IE_CEL_PEER] = { AST_EVENT_IE_PLTYPE_STR, "CELPeer" },
+ [AST_EVENT_IE_CEL_LINKEDID] = { AST_EVENT_IE_PLTYPE_STR, "CELLinkedID" },
+ [AST_EVENT_IE_CEL_PEERACCT] = { AST_EVENT_IE_PLTYPE_STR, "CELPeerAcct" },
+ [AST_EVENT_IE_CEL_EXTRA] = { AST_EVENT_IE_PLTYPE_STR, "CELExtra" },
+ [AST_EVENT_IE_SECURITY_EVENT] = { AST_EVENT_IE_PLTYPE_STR, "SecurityEvent" },
+ [AST_EVENT_IE_EVENT_VERSION] = { AST_EVENT_IE_PLTYPE_UINT, "EventVersion" },
+ [AST_EVENT_IE_SERVICE] = { AST_EVENT_IE_PLTYPE_STR, "Service" },
+ [AST_EVENT_IE_MODULE] = { AST_EVENT_IE_PLTYPE_STR, "Module" },
+ [AST_EVENT_IE_ACCOUNT_ID] = { AST_EVENT_IE_PLTYPE_STR, "AccountID" },
+ [AST_EVENT_IE_SESSION_ID] = { AST_EVENT_IE_PLTYPE_STR, "SessionID" },
+ [AST_EVENT_IE_SESSION_TV] = { AST_EVENT_IE_PLTYPE_STR, "SessionTV" },
+ [AST_EVENT_IE_ACL_NAME] = { AST_EVENT_IE_PLTYPE_STR, "ACLName" },
+ [AST_EVENT_IE_LOCAL_ADDR] = { AST_EVENT_IE_PLTYPE_STR, "LocalAddress" },
+ [AST_EVENT_IE_REMOTE_ADDR] = { AST_EVENT_IE_PLTYPE_STR, "RemoteAddress" },
+ [AST_EVENT_IE_EVENT_TV] = { AST_EVENT_IE_PLTYPE_STR, "EventTV" },
+ [AST_EVENT_IE_REQUEST_TYPE] = { AST_EVENT_IE_PLTYPE_STR, "RequestType" },
+ [AST_EVENT_IE_REQUEST_PARAMS] = { AST_EVENT_IE_PLTYPE_STR, "RequestParams" },
+ [AST_EVENT_IE_AUTH_METHOD] = { AST_EVENT_IE_PLTYPE_STR, "AuthMethod" },
+ [AST_EVENT_IE_SEVERITY] = { AST_EVENT_IE_PLTYPE_STR, "Severity" },
+ [AST_EVENT_IE_EXPECTED_ADDR] = { AST_EVENT_IE_PLTYPE_STR, "ExpectedAddress" },
+ [AST_EVENT_IE_CHALLENGE] = { AST_EVENT_IE_PLTYPE_STR, "Challenge" },
+ [AST_EVENT_IE_RESPONSE] = { AST_EVENT_IE_PLTYPE_STR, "Response" },
+ [AST_EVENT_IE_EXPECTED_RESPONSE] = { AST_EVENT_IE_PLTYPE_STR, "ExpectedResponse" },
};
const char *ast_event_get_type_name(const struct ast_event *event)
@@ -248,12 +262,12 @@ const char *ast_event_get_type_name(const struct ast_event *event)
type = ast_event_get_type(event);
- if (type >= AST_EVENT_TOTAL || type < 0) {
+ if (type < 0 || type >= ARRAY_LEN(event_names)) {
ast_log(LOG_ERROR, "Invalid event type - '%d'\n", type);
return "";
}
- return event_names[type].name;
+ return event_names[type];
}
int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type)
@@ -261,10 +275,11 @@ int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type
int i;
for (i = 0; i < ARRAY_LEN(event_names); i++) {
- if (strcasecmp(event_names[i].name, str))
+ if (strcasecmp(event_names[i], str)) {
continue;
+ }
- *event_type = event_names[i].type;
+ *event_type = i;
return 0;
}
@@ -273,31 +288,21 @@ int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type
const char *ast_event_get_ie_type_name(enum ast_event_ie_type ie_type)
{
- if (ie_type <= 0 || ie_type > AST_EVENT_IE_MAX) {
+ if (ie_type <= 0 || ie_type >= ARRAY_LEN(ie_maps)) {
ast_log(LOG_ERROR, "Invalid IE type - '%d'\n", ie_type);
return "";
}
- if (ie_maps[ie_type].ie_type != ie_type) {
- ast_log(LOG_ERROR, "The ie type passed in does not match the ie type defined in the ie table.\n");
- return "";
- }
-
return ie_maps[ie_type].name;
}
enum ast_event_ie_pltype ast_event_get_ie_pltype(enum ast_event_ie_type ie_type)
{
- if (ie_type <= 0 || ie_type > AST_EVENT_IE_MAX) {
+ if (ie_type <= 0 || ie_type >= ARRAY_LEN(ie_maps)) {
ast_log(LOG_ERROR, "Invalid IE type - '%d'\n", ie_type);
return AST_EVENT_IE_PLTYPE_UNKNOWN;
}
- if (ie_maps[ie_type].ie_type != ie_type) {
- ast_log(LOG_ERROR, "The ie type passed in does not match the ie type defined in the ie table.\n");
- return AST_EVENT_IE_PLTYPE_UNKNOWN;
- }
-
return ie_maps[ie_type].ie_pltype;
}
@@ -306,10 +311,11 @@ int ast_event_str_to_ie_type(const char *str, enum ast_event_ie_type *ie_type)
int i;
for (i = 0; i < ARRAY_LEN(ie_maps); i++) {
- if (strcasecmp(ie_maps[i].name, str))
+ if (strcasecmp(ie_maps[i].name, str)) {
continue;
+ }
- *ie_type = ie_maps[i].ie_type;
+ *ie_type = i;
return 0;
}
@@ -661,7 +667,7 @@ int ast_event_sub_append_ie_uint(struct ast_event_sub *sub,
{
struct ast_event_ie_val *ie_val;
- if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) {
+ if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) {
return -1;
}
@@ -683,11 +689,13 @@ int ast_event_sub_append_ie_bitflags(struct ast_event_sub *sub,
{
struct ast_event_ie_val *ie_val;
- if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX)
+ if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) {
return -1;
+ }
- if (!(ie_val = ast_calloc(1, sizeof(*ie_val))))
+ if (!(ie_val = ast_calloc(1, sizeof(*ie_val)))) {
return -1;
+ }
ie_val->ie_type = ie_type;
ie_val->payload.uint = flags;
@@ -703,7 +711,7 @@ int ast_event_sub_append_ie_exists(struct ast_event_sub *sub,
{
struct ast_event_ie_val *ie_val;
- if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) {
+ if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) {
return -1;
}
@@ -724,7 +732,7 @@ int ast_event_sub_append_ie_str(struct ast_event_sub *sub,
{
struct ast_event_ie_val *ie_val;
- if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) {
+ if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) {
return -1;
}
@@ -752,7 +760,7 @@ int ast_event_sub_append_ie_raw(struct ast_event_sub *sub,
{
struct ast_event_ie_val *ie_val;
- if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) {
+ if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) {
return -1;
}
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;
diff --git a/main/security_events.c b/main/security_events.c
new file mode 100644
index 000000000..d1e2ac0cc
--- /dev/null
+++ b/main/security_events.c
@@ -0,0 +1,647 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Russell Bryant <russell@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Security Event Reporting Helpers
+ *
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/utils.h"
+#include "asterisk/strings.h"
+#include "asterisk/network.h"
+#include "asterisk/security_events.h"
+
+static const size_t TIMESTAMP_STR_LEN = 32;
+
+static const struct {
+ const char *name;
+ uint32_t version;
+ enum ast_security_event_severity severity;
+#define MAX_SECURITY_IES 12
+ struct ast_security_event_ie_type required_ies[MAX_SECURITY_IES];
+ struct ast_security_event_ie_type optional_ies[MAX_SECURITY_IES];
+#undef MAX_SECURITY_IES
+} sec_events[AST_SECURITY_EVENT_NUM_TYPES] = {
+
+#define SEC_EVT_FIELD(e, field) (offsetof(struct ast_security_event_##e, field))
+
+[AST_SECURITY_EVENT_FAILED_ACL] = {
+ .name = "FailedACL",
+ .version = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_ACL_NAME, SEC_EVT_FIELD(failed_acl, acl_name) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_INVAL_ACCT_ID] = {
+ .name = "InvalidAccountID",
+ .version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_SESSION_LIMIT] = {
+ .name = "SessionLimit",
+ .version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_MEM_LIMIT] = {
+ .name = "MemoryLimit",
+ .version = AST_SECURITY_EVENT_MEM_LIMIT_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_LOAD_AVG] = {
+ .name = "LoadAverageLimit",
+ .version = AST_SECURITY_EVENT_LOAD_AVG_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_REQ_NO_SUPPORT] = {
+ .name = "RequestNotSupported",
+ .version = AST_SECURITY_EVENT_REQ_NO_SUPPORT_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_no_support, request_type) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_REQ_NOT_ALLOWED] = {
+ .name = "RequestNotAllowed",
+ .version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_not_allowed, request_type) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_REQUEST_PARAMS, SEC_EVT_FIELD(req_not_allowed, request_params) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED] = {
+ .name = "AuthMethodNotAllowed",
+ .version = AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_AUTH_METHOD, SEC_EVT_FIELD(auth_method_not_allowed, auth_method) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_REQ_BAD_FORMAT] = {
+ .name = "RequestBadFormat",
+ .version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_bad_format, request_type) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_REQUEST_PARAMS, SEC_EVT_FIELD(req_bad_format, request_params) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_SUCCESSFUL_AUTH] = {
+ .name = "SuccessfulAuth",
+ .version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_INFO,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_UNEXPECTED_ADDR] = {
+ .name = "UnexpectedAddress",
+ .version = AST_SECURITY_EVENT_UNEXPECTED_ADDR_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_EXPECTED_ADDR, SEC_EVT_FIELD(unexpected_addr, expected_addr) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_CHAL_RESP_FAILED] = {
+ .name = "ChallengeResponseFailed",
+ .version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_CHALLENGE, SEC_EVT_FIELD(chal_resp_failed, challenge) },
+ { AST_EVENT_IE_RESPONSE, SEC_EVT_FIELD(chal_resp_failed, response) },
+ { AST_EVENT_IE_EXPECTED_RESPONSE, SEC_EVT_FIELD(chal_resp_failed, expected_response) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+[AST_SECURITY_EVENT_INVAL_PASSWORD] = {
+ .name = "InvalidPassword",
+ .version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION,
+ .severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+ .required_ies = {
+ { AST_EVENT_IE_EVENT_TV, 0 },
+ { AST_EVENT_IE_SEVERITY, 0 },
+ { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+ { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+ { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+ { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+ { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+ { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+ { AST_EVENT_IE_END, 0 }
+ },
+ .optional_ies = {
+ { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+ { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+ { AST_EVENT_IE_END, 0 }
+ },
+},
+
+#undef SEC_EVT_FIELD
+
+};
+
+static const struct {
+ enum ast_security_event_severity severity;
+ const char *str;
+} severities[] = {
+ { AST_SECURITY_EVENT_SEVERITY_INFO, "Informational" },
+ { AST_SECURITY_EVENT_SEVERITY_ERROR, "Error" },
+};
+
+const char *ast_security_event_severity_get_name(
+ const enum ast_security_event_severity severity)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LEN(severities); i++) {
+ if (severities[i].severity == severity) {
+ return severities[i].str;
+ }
+ }
+
+ return NULL;
+}
+
+static int check_event_type(const enum ast_security_event_type event_type)
+{
+ if (event_type < 0 || event_type >= AST_SECURITY_EVENT_NUM_TYPES) {
+ ast_log(LOG_ERROR, "Invalid security event type %u\n", event_type);
+ return -1;
+ }
+
+ return 0;
+}
+
+const char *ast_security_event_get_name(const enum ast_security_event_type event_type)
+{
+ if (check_event_type(event_type)) {
+ return NULL;
+ }
+
+ return sec_events[event_type].name;
+}
+
+const struct ast_security_event_ie_type *ast_security_event_get_required_ies(
+ const enum ast_security_event_type event_type)
+{
+ if (check_event_type(event_type)) {
+ return NULL;
+ }
+
+ return sec_events[event_type].required_ies;
+}
+
+const struct ast_security_event_ie_type *ast_security_event_get_optional_ies(
+ const enum ast_security_event_type event_type)
+{
+ if (check_event_type(event_type)) {
+ return NULL;
+ }
+
+ return sec_events[event_type].optional_ies;
+}
+
+static void encode_timestamp(struct ast_str **str, const struct timeval *tv)
+{
+ ast_str_set(str, 0, "%u-%u",
+ (unsigned int) tv->tv_sec,
+ (unsigned int) tv->tv_usec);
+}
+
+static struct ast_event *alloc_event(const struct ast_security_event_common *sec)
+{
+ struct ast_str *str = ast_str_alloca(TIMESTAMP_STR_LEN);
+ struct timeval tv = ast_tvnow();
+ const char *severity_str;
+
+ if (check_event_type(sec->event_type)) {
+ return NULL;
+ }
+
+ encode_timestamp(&str, &tv);
+
+ severity_str = S_OR(
+ ast_security_event_severity_get_name(sec_events[sec->event_type].severity),
+ "Unknown"
+ );
+
+ return ast_event_new(AST_EVENT_SECURITY,
+ AST_EVENT_IE_SECURITY_EVENT, AST_EVENT_IE_PLTYPE_UINT, sec->event_type,
+ AST_EVENT_IE_EVENT_VERSION, AST_EVENT_IE_PLTYPE_UINT, sec->version,
+ AST_EVENT_IE_EVENT_TV, AST_EVENT_IE_PLTYPE_STR, str->str,
+ AST_EVENT_IE_SERVICE, AST_EVENT_IE_PLTYPE_STR, sec->service,
+ AST_EVENT_IE_SEVERITY, AST_EVENT_IE_PLTYPE_STR, severity_str,
+ AST_EVENT_IE_END);
+}
+
+static int add_timeval_ie(struct ast_event **event, enum ast_event_ie_type ie_type,
+ const struct timeval *tv)
+{
+ struct ast_str *str = ast_str_alloca(TIMESTAMP_STR_LEN);
+
+ encode_timestamp(&str, tv);
+
+ return ast_event_append_ie_str(event, ie_type, ast_str_buffer(str));
+}
+
+static int add_ipv4_ie(struct ast_event **event, enum ast_event_ie_type ie_type,
+ const struct ast_security_event_ipv4_addr *addr)
+{
+ struct ast_str *str = ast_str_alloca(64);
+
+ ast_str_set(&str, 0, "IPV4/");
+
+ switch (addr->transport) {
+ case AST_SECURITY_EVENT_TRANSPORT_UDP:
+ ast_str_append(&str, 0, "UDP/");
+ break;
+ case AST_SECURITY_EVENT_TRANSPORT_TCP:
+ ast_str_append(&str, 0, "TCP/");
+ break;
+ case AST_SECURITY_EVENT_TRANSPORT_TLS:
+ ast_str_append(&str, 0, "TLS/");
+ break;
+ }
+
+ ast_str_append(&str, 0, "%s/%hu",
+ ast_inet_ntoa(addr->sin->sin_addr),
+ ntohs(addr->sin->sin_port));
+
+ return ast_event_append_ie_str(event, ie_type, ast_str_buffer(str));
+}
+
+enum ie_required {
+ NOT_REQUIRED,
+ REQUIRED
+};
+
+static int add_ie(struct ast_event **event, const struct ast_security_event_common *sec,
+ const struct ast_security_event_ie_type *ie_type, enum ie_required req)
+{
+ int res = 0;
+
+ switch (ie_type->ie_type) {
+ case AST_EVENT_IE_SERVICE:
+ case AST_EVENT_IE_ACCOUNT_ID:
+ case AST_EVENT_IE_SESSION_ID:
+ case AST_EVENT_IE_MODULE:
+ case AST_EVENT_IE_ACL_NAME:
+ case AST_EVENT_IE_REQUEST_TYPE:
+ case AST_EVENT_IE_REQUEST_PARAMS:
+ case AST_EVENT_IE_AUTH_METHOD:
+ case AST_EVENT_IE_CHALLENGE:
+ case AST_EVENT_IE_RESPONSE:
+ case AST_EVENT_IE_EXPECTED_RESPONSE:
+ {
+ const char *str;
+
+ str = *((const char **)(((const char *) sec) + ie_type->offset));
+
+ if (req && !str) {
+ ast_log(LOG_WARNING, "Required IE '%d' for security event "
+ "type '%d' not present\n", ie_type->ie_type,
+ sec->event_type);
+ res = -1;
+ }
+
+ if (str) {
+ res = ast_event_append_ie_str(event, ie_type->ie_type, str);
+ }
+
+ break;
+ }
+ case AST_EVENT_IE_EVENT_VERSION:
+ {
+ uint32_t val;
+ val = *((const uint32_t *)(((const char *) sec) + ie_type->offset));
+ res = ast_event_append_ie_uint(event, ie_type->ie_type, val);
+ break;
+ }
+ case AST_EVENT_IE_LOCAL_ADDR:
+ case AST_EVENT_IE_REMOTE_ADDR:
+ case AST_EVENT_IE_EXPECTED_ADDR:
+ {
+ const struct ast_security_event_ipv4_addr *addr;
+
+ addr = (const struct ast_security_event_ipv4_addr *)(((const char *) sec) + ie_type->offset);
+
+ if (req && !addr->sin) {
+ ast_log(LOG_WARNING, "Required IE '%d' for security event "
+ "type '%d' not present\n", ie_type->ie_type,
+ sec->event_type);
+ res = -1;
+ }
+
+ if (addr->sin) {
+ res = add_ipv4_ie(event, ie_type->ie_type, addr);
+ }
+ break;
+ }
+ case AST_EVENT_IE_SESSION_TV:
+ {
+ const struct timeval *tval;
+
+ tval = *((const struct timeval **)(((const char *) sec) + ie_type->offset));
+
+ if (req && !tval) {
+ ast_log(LOG_WARNING, "Required IE '%d' for security event "
+ "type '%d' not present\n", ie_type->ie_type,
+ sec->event_type);
+ res = -1;
+ }
+
+ if (tval) {
+ add_timeval_ie(event, ie_type->ie_type, tval);
+ }
+
+ break;
+ }
+ case AST_EVENT_IE_EVENT_TV:
+ case AST_EVENT_IE_SEVERITY:
+ /* Added automatically, nothing to do here. */
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unhandled IE type '%d', this security event "
+ "will be missing data.\n", ie_type->ie_type);
+ break;
+ }
+
+ return res;
+}
+
+static int handle_security_event(const struct ast_security_event_common *sec)
+{
+ struct ast_event *event;
+ const struct ast_security_event_ie_type *ies;
+ unsigned int i;
+
+ if (!(event = alloc_event(sec))) {
+ return -1;
+ }
+
+ for (ies = ast_security_event_get_required_ies(sec->event_type), i = 0;
+ ies[i].ie_type != AST_EVENT_IE_END;
+ i++) {
+ if (add_ie(&event, sec, ies + i, REQUIRED)) {
+ goto return_error;
+ }
+ }
+
+ for (ies = ast_security_event_get_optional_ies(sec->event_type), i = 0;
+ ies[i].ie_type != AST_EVENT_IE_END;
+ i++) {
+ if (add_ie(&event, sec, ies + i, NOT_REQUIRED)) {
+ goto return_error;
+ }
+ }
+
+
+ if (ast_event_queue(event)) {
+ goto return_error;
+ }
+
+ return 0;
+
+return_error:
+ if (event) {
+ ast_event_destroy(event);
+ }
+
+ return -1;
+}
+
+int ast_security_event_report(const struct ast_security_event_common *sec)
+{
+ int res;
+
+ if (sec->event_type < 0 || sec->event_type >= AST_SECURITY_EVENT_NUM_TYPES) {
+ ast_log(LOG_ERROR, "Invalid security event type\n");
+ return -1;
+ }
+
+ if (!sec_events[sec->event_type].name) {
+ ast_log(LOG_WARNING, "Security event type %u not handled\n",
+ sec->event_type);
+ return -1;
+ }
+
+ if (sec->version != sec_events[sec->event_type].version) {
+ ast_log(LOG_WARNING, "Security event %u version mismatch\n",
+ sec->event_type);
+ return -1;
+ }
+
+ res = handle_security_event(sec);
+
+ return res;
+}
+
+