asterisk/main/security_events.c
Russell Bryant 4cf8a968fd 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
2009-07-11 19:15:03 +00:00

648 lines
20 KiB
C

/*
* 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;
}