aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/tex/asterisk.tex3
-rw-r--r--doc/tex/security-events.tex250
-rw-r--r--include/asterisk/event_defs.h97
-rw-r--r--include/asterisk/security_events.h114
-rw-r--r--include/asterisk/security_events_defs.h470
-rw-r--r--main/event.c156
-rw-r--r--main/manager.c267
-rw-r--r--main/security_events.c647
-rw-r--r--res/res_security_log.c163
-rwxr-xr-xtests/test_ami_security_events.sh47
-rw-r--r--tests/test_security_events.c625
11 files changed, 2723 insertions, 116 deletions
diff --git a/doc/tex/asterisk.tex b/doc/tex/asterisk.tex
index 3900f4cb6..935d0506a 100644
--- a/doc/tex/asterisk.tex
+++ b/doc/tex/asterisk.tex
@@ -144,6 +144,9 @@ reference purposes.
\chapter{Calendaring}
\input{calendaring.tex}
+\chapter{Security Framework}
+ \input{security-events.tex}
+
\chapter{Development}
\section{Backtrace}
\input{backtrace.tex}
diff --git a/doc/tex/security-events.tex b/doc/tex/security-events.tex
new file mode 100644
index 000000000..d46b1505d
--- /dev/null
+++ b/doc/tex/security-events.tex
@@ -0,0 +1,250 @@
+\section{Introduction}
+
+ Attacks on Voice over IP networks are becoming increasingly more common. It
+has become clear that we must do something within Asterisk to help mitigate
+these attacks.
+
+ Through a number of discussions with groups of developers in the Asterisk
+community, the general consensus is that the best thing that we can do within
+Asterisk is to build a framework which recognizes and reports events that could
+potentially have security implications. Each channel driver has a different
+concept of what is an "event", and then each administrator has different
+thresholds of what is a "bad" event and what is a restorative event. The
+process of acting upon this information is left to an external program to
+correlate and then take action - block traffic, modify dialing rules, etc. It
+was decided that embedding actions inside of Asterisk was inappropriate, as the
+complexity of construction of such rule sets is difficult and there was no
+agreement on where rules should be enabled or how they should be processed. The
+addition of a major section of code to handle rule expiration and severity
+interpretation was significant. As a final determining factor, there are
+external programs and services which already parse log files and act in concert
+with packet filters or external devices to protect or alter network security
+models for IP connected hosts.
+
+\section{Framework Overview}
+
+ This section discusses the architecture of the Asterisk modifications being
+proposed.
+
+ There are two main components that we propose for the initial
+implementation of the security framework:
+
+\begin{itemize}
+ \item Security Event Generation
+ \item Security Event Logger
+\end{itemize}
+
+\subsection{Security Event Generation}
+
+ The ast\_event API is used for the generation of security events. That
+way, the events are in an easily interpretable format within Asterisk to
+make it easy to write modules that do things with them. There are also some
+helper data structures and functions to aid Asterisk modules in reporting these
+security events with the proper contents.
+
+ The next section of this document contains the current list of security events
+being proposed. Each security event type has some required pieces of
+information and some other optional pieces of information.
+
+ Subscribing to security events from within Asterisk can be done by
+subscribing to events of type AST\_EVENT\_SECURITY. These events have an
+information element, AST\_EVENT\_IE\_SECURITY\_EVENT, which identifies the security
+event sub-type (from the list described in the next section). The result of the
+information elements in the events contain the required and optional meta data
+associated with the event sub-type.
+
+\subsection{Security Event Logger}
+
+ In addition to the infrastructure for generating the events, one module that
+is a consumer of these events has been implemented.
+
+ Asterisk trunk was recently updated to include support for dynamic logger
+levels. This module takes advantage of this functionality to create a
+custom "security" logger level. Then, when this module is in use, logger.conf
+can be configured to put security events into a file:
+
+\begin{verbatim}
+ security_log => security
+\end{verbatim}
+
+ The content of this file is a well defined and easily interpretable
+format for external scripts to read and act upon. The definition for the format
+of the log file is described later in this chapter.
+
+\section{Events to Log}
+
+\begin{verbatim}
+(-) required
+(+) optional
+
+Invalid Account ID
+ (-) Local address family/IP address/port/transport
+ (-) Remote address family/IP address/port/transport
+ (-) Service (SIP, AMI, IAX2, ...)
+ (-) System Name
+ (+) Module
+ (+) Account ID (username, etc)
+ (+) Session ID (CallID, etc)
+ (+) Session timestamp (required if Session ID present)
+ (-) Event timestamp (sub-second precision)
+
+Failed ACL match
+ -> everything from invalid account ID
+ (+) Name of ACL (when we have named ACLs)
+
+Invalid Challenge/Response
+ -> everything from invalid account ID
+ (-) Challenge
+ (-) Response
+ (-) Expected Response
+
+Invalid Password
+ -> everything from invalid account ID
+
+Successful Authentication
+ -> informational event
+ -> everything from invalid account ID
+
+Invalid formatting of Request
+ -> everything from invalid account ID
+ -> account ID optional
+ (-) Request Type
+ (+) Request parameters
+
+Session Limit Reached (such as a call limit)
+ -> everything from invalid account ID
+
+Memory Limit Reached
+ -> everything from invalid account ID
+
+Maximum Load Average Reached
+ -> everything from invalid account ID
+
+Request Not Allowed
+ -> everything from invalid account ID
+ (-) Request Type
+ (+) Request parameters
+
+Request Not Supported
+ -> everything from invalid account ID
+ (-) Request Type
+
+Authentication Method Not Allowed
+ -> everything from invalid account ID
+ (-) Authentication Method attempted
+
+In dialog message from unexpected host
+ -> everything from invalid account ID
+ (-) expected host
+\end{verbatim}
+
+\section{Security Log File Format}
+
+ The beginning of each line in the log file is the same as it is for other
+logger levels within Asterisk.
+
+\begin{verbatim}
+ [Feb 11 07:57:03] SECURITY[23736] res_security_log.c: <...>
+\end{verbatim}
+
+ The part of the log entry identified by <...> is where the security event
+content resides. The security event content is a comma separated list
+of key value pairs. The key is the information element type, and the value is a
+quoted string that contains the associated meta data for that information
+element. Any embedded quotes within the content are escaped with a
+backslash.
+
+\begin{verbatim}
+ INFORMATION_ELEMENT_1="IE1 content",INFORMATION_ELEMENT_2="IE2 content"
+\end{verbatim}
+
+The following table includes potential information elements and what the
+associated content looks like:
+
+\begin{verbatim}
+IE: SecurityEvent
+Content: This is the security event sub-type.
+Values: FailedACL, InvalidAccountID, SessionLimit, MemoryLimit, LoadAverageLimit,
+ RequestNotSupported, RequestNotAllowed, AuthMethodNotAllowed,
+ ReqBadFormat, UnexpectedAddress, ChallengeResponseFailed,
+ InvalidPassword
+
+IE: EventVersion
+Content: This is a numeric value that indicates when updates are made to the
+ content of the event.
+Values: Monotonically increasing integer, starting at 1
+
+IE: Service
+Content: This is the Asterisk service that generated the event.
+Values: TEST, SIP, AMI
+
+IE: Module
+Content: This is the Asterisk module that generated the event.
+Values: chan_sip
+
+IE: AccountID
+Content: This is a string used to identify the account associated with the
+ event. In most cases, this would be a username.
+
+IE: SessionID
+Content: This is a string used to identify the session associated with the
+ event. The format of the session identifier is specific to the
+ service. In the case of SIP, this would be the Call-ID.
+
+IE: SessionTV
+Content: The time that the session associated with the SessionID started.
+Values: <seconds>-<microseconds> since epoch
+
+IE: ACLName
+Content: This is a string that identifies which named ACL is associated with
+ this event.
+
+IE: LocalAddress
+Content: This is the local address that was contacted for the related event.
+Values: <Address Family>/<Transport>/<Address>/<Port>
+Examples:
+ -> IPV4/UDP/192.168.1.1/5060
+ -> IPV4/TCP/192.168.1.1/5038
+
+IE: RemoteAddress
+Content: This is the remote address associated with the event.
+Examples:
+ -> IPV4/UDP/192.168.1.2/5060
+ -> IPV4/TCP/192.168.1.2/5038
+
+IE: ExpectedAddress
+Content: This is the address that was expected to be the remote address.
+Examples:
+ -> IPV4/UDP/192.168.1.2/5060
+ -> IPV4/TCP/192.168.1.2/5038
+
+IE: EventTV
+Content: This is the timestamp of when the event occurred.
+Values: <seconds>-<microseconds> since epoch
+
+IE: RequestType
+Content: This is a service specific string that represents the invalid request
+
+IE: RequestParams
+Content: This is a service specific string that represents relevant parameters
+ given with a request that was considered invalid.
+
+IE: AuthMethod
+Content: This is a service specific string that represents an authentication
+ method that was used or requested.
+
+IE: Challenge
+Content: This is a service specific string that represents the challenge
+ provided to a user attempting challenge/response authentication.
+
+IE: Response
+Content: This is a service specific string that represents the response
+ received from a user attempting challenge/response authentication.
+
+IE: ExpectedResponse
+Content: This is a service specific string that represents the response
+ that was expected to be received from a user attempting
+ challenge/response authentication.
+
+\end{verbatim}
+
diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h
index 99edb6f55..3779dac73 100644
--- a/include/asterisk/event_defs.h
+++ b/include/asterisk/event_defs.h
@@ -49,58 +49,60 @@ enum ast_event_type {
* directly, in general. Use AST_EVENT_DEVICE_STATE instead. */
AST_EVENT_DEVICE_STATE_CHANGE = 0x06,
/*! Channel Event Logging events */
- AST_EVENT_CEL = 0x07,
+ AST_EVENT_CEL = 0x07,
+ /*! A report of a security related event (see security_events.h) */
+ AST_EVENT_SECURITY = 0x08,
/*! Number of event types. This should be the last event type + 1 */
- AST_EVENT_TOTAL = 0x08,
+ AST_EVENT_TOTAL = 0x09,
};
/*! \brief Event Information Element types */
enum ast_event_ie_type {
/*! Used to terminate the arguments to event functions */
- AST_EVENT_IE_END = -1,
+ AST_EVENT_IE_END = -1,
/*!
* \brief Number of new messages
* Used by: AST_EVENT_MWI
* Payload type: UINT
*/
- AST_EVENT_IE_NEWMSGS = 0x01,
+ AST_EVENT_IE_NEWMSGS = 0x0001,
/*!
* \brief Number of
* Used by: AST_EVENT_MWI
* Payload type: UINT
*/
- AST_EVENT_IE_OLDMSGS = 0x02,
+ AST_EVENT_IE_OLDMSGS = 0x0002,
/*!
* \brief Mailbox name \verbatim (mailbox[@context]) \endverbatim
* Used by: AST_EVENT_MWI
* Payload type: STR
*/
- AST_EVENT_IE_MAILBOX = 0x03,
+ AST_EVENT_IE_MAILBOX = 0x0003,
/*!
* \brief Unique ID
* Used by: AST_EVENT_SUB, AST_EVENT_UNSUB
* Payload type: UINT
*/
- AST_EVENT_IE_UNIQUEID = 0x04,
+ AST_EVENT_IE_UNIQUEID = 0x0004,
/*!
* \brief Event type
* Used by: AST_EVENT_SUB, AST_EVENT_UNSUB
* Payload type: UINT
*/
- AST_EVENT_IE_EVENTTYPE = 0x05,
+ AST_EVENT_IE_EVENTTYPE = 0x0005,
/*!
* \brief Hint that someone cares that an IE exists
* Used by: AST_EVENT_SUB
* Payload type: UINT (ast_event_ie_type)
*/
- AST_EVENT_IE_EXISTS = 0x6,
+ AST_EVENT_IE_EXISTS = 0x0006,
/*!
* \brief Device Name
* Used by AST_EVENT_DEVICE_STATE_CHANGE
* Payload type: STR
*/
- AST_EVENT_IE_DEVICE = 0x07,
+ AST_EVENT_IE_DEVICE = 0x0007,
/*!
* \brief Generic State IE
* Used by AST_EVENT_DEVICE_STATE_CHANGE
@@ -108,162 +110,181 @@ enum ast_event_ie_type {
* The actual state values depend on the event which
* this IE is a part of.
*/
- AST_EVENT_IE_STATE = 0x08,
+ AST_EVENT_IE_STATE = 0x0008,
/*!
* \brief Context IE
* Used by AST_EVENT_MWI
* Payload type: str
*/
- AST_EVENT_IE_CONTEXT = 0x09,
+ AST_EVENT_IE_CONTEXT = 0x0009,
/*!
* \brief Channel Event Type
* Used by: AST_EVENT_CEL
* Payload type: UINT
*/
- AST_EVENT_IE_CEL_EVENT_TYPE = 0x0a,
+ AST_EVENT_IE_CEL_EVENT_TYPE = 0x000a,
/*!
* \brief Channel Event Time (seconds)
* Used by: AST_EVENT_CEL
* Payload type: UINT
*/
- AST_EVENT_IE_CEL_EVENT_TIME = 0x0b,
+ AST_EVENT_IE_CEL_EVENT_TIME = 0x000b,
/*!
* \brief Channel Event Time (micro-seconds)
* Used by: AST_EVENT_CEL
* Payload type: UINT
*/
- AST_EVENT_IE_CEL_EVENT_TIME_USEC = 0x0c,
+ AST_EVENT_IE_CEL_EVENT_TIME_USEC = 0x000c,
/*!
* \brief Channel Event User Event Name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_USEREVENT_NAME = 0x0d,
+ AST_EVENT_IE_CEL_USEREVENT_NAME = 0x000d,
/*!
* \brief Channel Event CID name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_CIDNAME = 0x0e,
+ AST_EVENT_IE_CEL_CIDNAME = 0x000e,
/*!
* \brief Channel Event CID num
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_CIDNUM = 0x0f,
+ AST_EVENT_IE_CEL_CIDNUM = 0x000f,
/*!
* \brief Channel Event extension name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_EXTEN = 0x10,
+ AST_EVENT_IE_CEL_EXTEN = 0x0010,
/*!
* \brief Channel Event context name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_CONTEXT = 0x11,
+ AST_EVENT_IE_CEL_CONTEXT = 0x0011,
/*!
* \brief Channel Event channel name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_CHANNAME = 0x12,
+ AST_EVENT_IE_CEL_CHANNAME = 0x0012,
/*!
* \brief Channel Event app name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_APPNAME = 0x13,
+ AST_EVENT_IE_CEL_APPNAME = 0x0013,
/*!
* \brief Channel Event app args/data
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_APPDATA = 0x14,
+ AST_EVENT_IE_CEL_APPDATA = 0x0014,
/*!
* \brief Channel Event AMA flags
* Used by: AST_EVENT_CEL
* Payload type: UINT
*/
- AST_EVENT_IE_CEL_AMAFLAGS = 0x15,
+ AST_EVENT_IE_CEL_AMAFLAGS = 0x0015,
/*!
* \brief Channel Event AccountCode
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_ACCTCODE = 0x16,
+ AST_EVENT_IE_CEL_ACCTCODE = 0x0016,
/*!
* \brief Channel Event UniqueID
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_UNIQUEID = 0x17,
+ AST_EVENT_IE_CEL_UNIQUEID = 0x0017,
/*!
* \brief Channel Event Userfield
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_USERFIELD = 0x18,
+ AST_EVENT_IE_CEL_USERFIELD = 0x0018,
/*!
* \brief Channel Event CID ANI field
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_CIDANI = 0x19,
+ AST_EVENT_IE_CEL_CIDANI = 0x0019,
/*!
* \brief Channel Event CID RDNIS field
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_CIDRDNIS = 0x1a,
+ AST_EVENT_IE_CEL_CIDRDNIS = 0x001a,
/*!
* \brief Channel Event CID dnid
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_CIDDNID = 0x1b,
+ AST_EVENT_IE_CEL_CIDDNID = 0x001b,
/*!
* \brief Channel Event Peer -- for Things involving multiple channels, like BRIDGE
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_PEER = 0x1c,
+ AST_EVENT_IE_CEL_PEER = 0x001c,
/*!
* \brief Channel Event LinkedID
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_LINKEDID = 0x1d,
+ AST_EVENT_IE_CEL_LINKEDID = 0x001d,
/*!
* \brief Channel Event peeraccount
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_PEERACCT = 0x1e,
+ AST_EVENT_IE_CEL_PEERACCT = 0x001e,
/*!
* \brief Channel Event extra data
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
- AST_EVENT_IE_CEL_EXTRA = 0x1f,
+ AST_EVENT_IE_CEL_EXTRA = 0x001f,
/*!
* \brief Description
* Used by: AST_EVENT_SUB, AST_EVENT_UNSUB
* Payload type: STR
*/
- AST_EVENT_IE_DESCRIPTION = 0x20,
+ AST_EVENT_IE_DESCRIPTION = 0x0020,
/*!
* \brief Entity ID
* Used by All events
* Payload type: RAW
* This IE indicates which server the event originated from
*/
- AST_EVENT_IE_EID = 0x21,
+ AST_EVENT_IE_EID = 0x0021,
+ AST_EVENT_IE_SECURITY_EVENT = 0x0022,
+ AST_EVENT_IE_EVENT_VERSION = 0x0023,
+ AST_EVENT_IE_SERVICE = 0x0024,
+ AST_EVENT_IE_MODULE = 0x0025,
+ AST_EVENT_IE_ACCOUNT_ID = 0x0026,
+ AST_EVENT_IE_SESSION_ID = 0x0027,
+ AST_EVENT_IE_SESSION_TV = 0x0028,
+ AST_EVENT_IE_ACL_NAME = 0x0029,
+ AST_EVENT_IE_LOCAL_ADDR = 0x002a,
+ AST_EVENT_IE_REMOTE_ADDR = 0x002b,
+ AST_EVENT_IE_EVENT_TV = 0x002c,
+ AST_EVENT_IE_REQUEST_TYPE = 0x002d,
+ AST_EVENT_IE_REQUEST_PARAMS = 0x002e,
+ AST_EVENT_IE_AUTH_METHOD = 0x002f,
+ AST_EVENT_IE_SEVERITY = 0x0030,
+ AST_EVENT_IE_EXPECTED_ADDR = 0x0031,
+ AST_EVENT_IE_CHALLENGE = 0x0032,
+ AST_EVENT_IE_RESPONSE = 0x0033,
+ AST_EVENT_IE_EXPECTED_RESPONSE = 0x0034,
+ /*! \brief Must be the last IE value +1 */
+ AST_EVENT_IE_TOTAL = 0x0035,
};
-#define AST_EVENT_IE_MAX AST_EVENT_IE_EID
-
/*!
* \brief Payload types for event information elements
*/
diff --git a/include/asterisk/security_events.h b/include/asterisk/security_events.h
new file mode 100644
index 000000000..c15d04f0e
--- /dev/null
+++ b/include/asterisk/security_events.h
@@ -0,0 +1,114 @@
+/*
+ * 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 API
+ *
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+#ifndef __AST_SECURITY_EVENTS_H__
+#define __AST_SECURITY_EVENTS_H__
+
+#include "asterisk/event.h"
+
+/* Data structure definitions */
+#include "asterisk/security_events_defs.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*!
+ * \brief Report a security event
+ *
+ * \param[in] sec security event data. Callers of this function should never
+ * declare an instance of ast_security_event_common directly. The
+ * argument should be an instance of a specific security event
+ * descriptor which has ast_security_event_common at the very
+ * beginning.
+ *
+ * \retval 0 success
+ * \retval non-zero failure
+ */
+int ast_security_event_report(const struct ast_security_event_common *sec);
+
+struct ast_security_event_ie_type {
+ enum ast_event_ie_type ie_type;
+ /*! \brief For internal usage */
+ size_t offset;
+};
+
+/*!
+ * \brief Get the list of required IEs for a given security event sub-type
+ *
+ * \param[in] event_type security event sub-type
+ *
+ * \retval NULL invalid event_type
+ * \retval non-NULL An array terminated with the value AST_EVENT_IE_END
+ *
+ * \since 1.6.3
+ */
+const struct ast_security_event_ie_type *ast_security_event_get_required_ies(
+ const enum ast_security_event_type event_type);
+
+/*!
+ * \brief Get the list of optional IEs for a given security event sub-type
+ *
+ * \param[in] event_type security event sub-type
+ *
+ * \retval NULL invalid event_type
+ * \retval non-NULL An array terminated with the value AST_EVENT_IE_END
+ *
+ * \since 1.6.3
+ */
+const struct ast_security_event_ie_type *ast_security_event_get_optional_ies(
+ const enum ast_security_event_type event_type);
+
+/*!
+ * \brief Get the name of a security event sub-type
+ *
+ * \param[in] event_type security event sub-type
+ *
+ * \retval NULL if event_type is invalid
+ * \retval non-NULL the name of the security event type
+ *
+ * \since 1.6.3
+ */
+const char *ast_security_event_get_name(const enum ast_security_event_type event_type);
+
+/*!
+ * \brief Get the name of a security event severity
+ *
+ * \param[in] severity security event severity
+ *
+ * \retval NULL if severity is invalid
+ * \retval non-NULL the name of the security event severity
+ *
+ * \since 1.6.3
+ */
+const char *ast_security_event_severity_get_name(
+ const enum ast_security_event_severity severity);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* __AST_SECURITY_EVENTS_H__ */
diff --git a/include/asterisk/security_events_defs.h b/include/asterisk/security_events_defs.h
new file mode 100644
index 000000000..e39cf312d
--- /dev/null
+++ b/include/asterisk/security_events_defs.h
@@ -0,0 +1,470 @@
+/*
+ * 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 Data Structures
+ *
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+#ifndef __AST_SECURITY_EVENTS_DEFS_H__
+#define __AST_SECURITY_EVENTS_DEFS_H__
+
+#include "asterisk/network.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*!
+ * \brief Security event types
+ *
+ * AST_EVENT_SECURITY is the event type of an ast_event generated as a security
+ * event. The event will have an information element of type
+ * AST_EVENT_IE_SECURITY_EVENT which identifies the security event sub-type.
+ * This enum defines the possible values for this sub-type.
+ */
+enum ast_security_event_type {
+ /*!
+ * \brief Failed ACL
+ *
+ * This security event should be generated when an incoming request
+ * was made, but was denied due to configured IP address access control
+ * lists.
+ */
+ AST_SECURITY_EVENT_FAILED_ACL,
+ /*!
+ * \brief Invalid Account ID
+ *
+ * This event is used when an invalid account identifier is supplied
+ * during authentication. For example, if an invalid username is given,
+ * this event should be used.
+ */
+ AST_SECURITY_EVENT_INVAL_ACCT_ID,
+ /*!
+ * \brief Session limit reached
+ *
+ * A request has been denied because a configured session limit has been
+ * reached, such as a call limit.
+ */
+ AST_SECURITY_EVENT_SESSION_LIMIT,
+ /*!
+ * \brief Memory limit reached
+ *
+ * A request has been denied because a configured memory limit has been
+ * reached.
+ */
+ AST_SECURITY_EVENT_MEM_LIMIT,
+ /*!
+ * \brief Load Average limit reached
+ *
+ * A request has been denied because a configured load average limit has been
+ * reached.
+ */
+ AST_SECURITY_EVENT_LOAD_AVG,
+ /*!
+ * \brief A request was made that we understand, but do not support
+ */
+ AST_SECURITY_EVENT_REQ_NO_SUPPORT,
+ /*!
+ * \brief A request was made that is not allowed
+ */
+ AST_SECURITY_EVENT_REQ_NOT_ALLOWED,
+ /*!
+ * \brief The attempted authentication method is not allowed
+ */
+ AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED,
+ /*!
+ * \brief Request received with bad formatting
+ */
+ AST_SECURITY_EVENT_REQ_BAD_FORMAT,
+ /*!
+ * \brief FYI FWIW, Successful authentication has occurred
+ */
+ AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
+ /*!
+ * \brief An unexpected source address was seen for a session in progress
+ */
+ AST_SECURITY_EVENT_UNEXPECTED_ADDR,
+ /*!
+ * \brief An attempt at challenge/response authentication failed
+ */
+ AST_SECURITY_EVENT_CHAL_RESP_FAILED,
+ /*!
+ * \brief An attempt at basic password authentication failed
+ */
+ AST_SECURITY_EVENT_INVAL_PASSWORD,
+ /* \brief This _must_ stay at the end. */
+ AST_SECURITY_EVENT_NUM_TYPES
+};
+
+/*!
+ * \brief the severity of a security event
+ *
+ * This is defined as a bit field to make it easy for consumers of the API to
+ * subscribe to any combination of the defined severity levels.
+ *
+ * XXX \todo Do we need any more levels here?
+ */
+enum ast_security_event_severity {
+ /*! \brief Informational event, not something that has gone wrong */
+ AST_SECURITY_EVENT_SEVERITY_INFO = (1 << 0),
+ /*! \brief Something has gone wrong */
+ AST_SECURITY_EVENT_SEVERITY_ERROR = (1 << 1),
+};
+
+/*!
+ * \brief Transport types
+ */
+enum ast_security_event_transport_type {
+ AST_SECURITY_EVENT_TRANSPORT_UDP,
+ AST_SECURITY_EVENT_TRANSPORT_TCP,
+ AST_SECURITY_EVENT_TRANSPORT_TLS,
+};
+
+#define AST_SEC_EVT(e) ((struct ast_security_event_common *) e)
+
+struct ast_security_event_ipv4_addr {
+ const struct sockaddr_in *sin;
+ enum ast_security_event_transport_type transport;
+};
+
+/*!
+ * \brief Common structure elements
+ *
+ * This is the structure header for all event descriptor structures defined
+ * below. The contents of this structure are very important and must not
+ * change. Even though these structures are exposed via a public API, we have
+ * a version field that can be used to ensure ABI safety. If the event
+ * descriptors need to be changed or updated in the future, we can safely do
+ * so and can detect ABI changes at runtime.
+ */
+struct ast_security_event_common {
+ /*! \brief The security event sub-type */
+ enum ast_security_event_type event_type;
+ /*! \brief security event version */
+ uint32_t version;
+ /*!
+ * \brief Service that generated the event
+ * \note Always required
+ *
+ * Examples: "SIP", "AMI"
+ */
+ const char *service;
+ /*!
+ * \brief Module, Normally the AST_MODULE define
+ * \note Always optional
+ */
+ const char *module;
+ /*!
+ * \brief Account ID, specific to the service type
+ * \note optional/required, depending on event type
+ */
+ const char *account_id;
+ /*!
+ * \brief Session ID, specific to the service type
+ * \note Always required
+ */
+ const char *session_id;
+ /*!
+ * \brief Session timeval, when the session started
+ * \note Always optional
+ */
+ const struct timeval *session_tv;
+ /*!
+ * \brief Local address the request came in on
+ * \note Always required
+ */
+ struct ast_security_event_ipv4_addr local_addr;
+ /*!
+ * \brief Remote address the request came from
+ * \note Always required
+ */
+ struct ast_security_event_ipv4_addr remote_addr;
+};
+
+/*!
+ * \brief Checking against an IP access control list failed
+ */
+struct ast_security_event_failed_acl {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_FAILED_ACL_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+ /*!
+ * \brief ACL name, identifies which ACL was hit
+ * \note optional
+ */
+ const char *acl_name;
+};
+
+/*!
+ * \brief Invalid account ID specified (invalid username, for example)
+ */
+struct ast_security_event_inval_acct_id {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+};
+
+/*!
+ * \brief Request denied because of a session limit
+ */
+struct ast_security_event_session_limit {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_SESSION_LIMIT_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+};
+
+/*!
+ * \brief Request denied because of a memory limit
+ */
+struct ast_security_event_mem_limit {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_MEM_LIMIT_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+};
+
+/*!
+ * \brief Request denied because of a load average limit
+ */
+struct ast_security_event_load_avg {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_LOAD_AVG_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+};
+
+/*!
+ * \brief Request denied because we don't support it
+ */
+struct ast_security_event_req_no_support {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_REQ_NO_SUPPORT_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+ /*!
+ * \brief Request type that was made
+ * \note required
+ */
+ const char *request_type;
+};
+
+/*!
+ * \brief Request denied because it's not allowed
+ */
+struct ast_security_event_req_not_allowed {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+ /*!
+ * \brief Request type that was made
+ * \note required
+ */
+ const char *request_type;
+ /*!
+ * \brief Request type that was made
+ * \note optional
+ */
+ const char *request_params;
+};
+
+/*!
+ * \brief Auth method used not allowed
+ */
+struct ast_security_event_auth_method_not_allowed {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+ /*!
+ * \brief Auth method attempted
+ * \note required
+ */
+ const char *auth_method;
+};
+
+/*!
+ * \brief Invalid formatting of request
+ */
+struct ast_security_event_req_bad_format {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID optional
+ */
+ struct ast_security_event_common common;
+ /*!
+ * \brief Request type that was made
+ * \note required
+ */
+ const char *request_type;
+ /*!
+ * \brief Request type that was made
+ * \note optional
+ */
+ const char *request_params;
+};
+
+/*!
+ * \brief Successful authentication
+ */
+struct ast_security_event_successful_auth {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+};
+
+/*!
+ * \brief Unexpected source address for a session in progress
+ */
+struct ast_security_event_unexpected_addr {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_UNEXPECTED_ADDR_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+ /*!
+ * \brief Expected remote address
+ * \note required
+ */
+ struct ast_security_event_ipv4_addr expected_addr;
+};
+
+/*!
+ * \brief An attempt at challenge/response auth failed
+ */
+struct ast_security_event_chal_resp_failed {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+ /*!
+ * \brief Challenge provided
+ * \note required
+ */
+ const char *challenge;
+ /*!
+ * \brief Response received
+ * \note required
+ */
+ const char *response;
+ /*!
+ * \brief Response expected to be received
+ * \note required
+ */
+ const char *expected_response;
+};
+
+/*!
+ * \brief An attempt at basic password auth failed
+ */
+struct ast_security_event_inval_password {
+ /*!
+ * \brief Event descriptor version
+ * \note This _must_ be changed if this event descriptor is changed.
+ */
+ #define AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION 1
+ /*!
+ * \brief Common security event descriptor elements
+ * \note Account ID required
+ */
+ struct ast_security_event_common common;
+};
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* __AST_SECURITY_EVENTS_DEFS_H__ */
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;
+}
+
+
diff --git a/res/res_security_log.c b/res/res_security_log.c
new file mode 100644
index 000000000..2e3d4af52
--- /dev/null
+++ b/res/res_security_log.c
@@ -0,0 +1,163 @@
+/*
+ * 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
+ *
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \brief Security Event Logging
+ *
+ * \todo Make informational security events optional
+ * \todo Escape quotes in string payload IE contents
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
+
+#include "asterisk/module.h"
+#include "asterisk/logger.h"
+#include "asterisk/event.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/strings.h"
+#include "asterisk/security_events.h"
+
+static const char LOG_SECURITY_NAME[] = "SECURITY";
+
+static int LOG_SECURITY;
+
+static struct ast_event_sub *security_event_sub;
+
+AST_THREADSTORAGE(security_event_buf);
+static const size_t SECURITY_EVENT_BUF_INIT_LEN = 256;
+
+enum ie_required {
+ NOT_REQUIRED,
+ REQUIRED
+};
+
+static int ie_is_present(const struct ast_event *event,
+ const enum ast_event_ie_type ie_type)
+{
+ return (ast_event_get_ie_raw(event, ie_type) != NULL);
+}
+
+static void append_ie(struct ast_str **str, const struct ast_event *event,
+ const enum ast_event_ie_type ie_type, enum ie_required required)
+{
+ if (!required && !ie_is_present(event, ie_type)) {
+ /* Optional IE isn't present. Ignore. */
+ return;
+ }
+
+ /* At this point, it _better_ be there! */
+ ast_assert(ie_is_present(event, ie_type));
+
+ switch (ast_event_get_ie_pltype(ie_type)) {
+ case AST_EVENT_IE_PLTYPE_UINT:
+ ast_str_append(str, 0, ",%s=\"%u\"",
+ ast_event_get_ie_type_name(ie_type),
+ ast_event_get_ie_uint(event, ie_type));
+ break;
+ case AST_EVENT_IE_PLTYPE_STR:
+ ast_str_append(str, 0, ",%s=\"%s\"",
+ ast_event_get_ie_type_name(ie_type),
+ ast_event_get_ie_str(event, ie_type));
+ break;
+ case AST_EVENT_IE_PLTYPE_BITFLAGS:
+ ast_str_append(str, 0, ",%s=\"%u\"",
+ ast_event_get_ie_type_name(ie_type),
+ ast_event_get_ie_bitflags(event, ie_type));
+ break;
+ case AST_EVENT_IE_PLTYPE_UNKNOWN:
+ case AST_EVENT_IE_PLTYPE_EXISTS:
+ case AST_EVENT_IE_PLTYPE_RAW:
+ ast_log(LOG_WARNING, "Unexpected payload type for IE '%s'\n",
+ ast_event_get_ie_type_name(ie_type));
+ break;
+ }
+}
+
+static void append_ies(struct ast_str **str, const struct ast_event *event,
+ const struct ast_security_event_ie_type *ies, enum ie_required required)
+{
+ unsigned int i;
+
+ for (i = 0; ies[i].ie_type != AST_EVENT_IE_END; i++) {
+ append_ie(str, event, ies[i].ie_type, required);
+ }
+}
+
+static void security_event_cb(const struct ast_event *event, void *data)
+{
+ struct ast_str *str;
+ enum ast_security_event_type event_type;
+
+ if (!(str = ast_str_thread_get(&security_event_buf,
+ SECURITY_EVENT_BUF_INIT_LEN))) {
+ return;
+ }
+
+ /* Note that the event type is guaranteed to be valid here. */
+ event_type = ast_event_get_ie_uint(event, AST_EVENT_IE_SECURITY_EVENT);
+ ast_assert(event_type >= 0 && event_type < AST_SECURITY_EVENT_NUM_TYPES);
+
+ ast_str_set(&str, 0, "%s=\"%s\"",
+ ast_event_get_ie_type_name(AST_EVENT_IE_SECURITY_EVENT),
+ ast_security_event_get_name(event_type));
+
+ append_ies(&str, event,
+ ast_security_event_get_required_ies(event_type), REQUIRED);
+ append_ies(&str, event,
+ ast_security_event_get_optional_ies(event_type), NOT_REQUIRED);
+
+ ast_log_dynamic_level(LOG_SECURITY, "%s\n", ast_str_buffer(str));
+}
+
+static int load_module(void)
+{
+ if ((LOG_SECURITY = ast_logger_register_level(LOG_SECURITY_NAME)) == -1) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (!(security_event_sub = ast_event_subscribe(AST_EVENT_SECURITY,
+ security_event_cb, "Security Event Logger",
+ NULL, AST_EVENT_IE_END))) {
+ ast_logger_unregister_level(LOG_SECURITY_NAME);
+ LOG_SECURITY = -1;
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ ast_verb(3, "Security Logging Enabled\n");
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ if (security_event_sub) {
+ security_event_sub = ast_event_unsubscribe(security_event_sub);
+ }
+
+ ast_verb(3, "Security Logging Disabled\n");
+
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Security Event Logging");
diff --git a/tests/test_ami_security_events.sh b/tests/test_ami_security_events.sh
new file mode 100755
index 000000000..6f125dc5a
--- /dev/null
+++ b/tests/test_ami_security_events.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# manager.conf:
+#
+# [general]
+# ...
+# allowmultipleconnects=no
+# ...
+#
+# [russell]
+# secret=blah123
+# read = system,call,log,verbose,command,agent,user,config
+# write = system,call,log,verbose,command,agent,user,config
+# deny=0.0.0.0/0.0.0.0
+# permit=127.0.0.1/255.255.255.255
+#
+# [russell2]
+# secret=blah123
+# read = system,call,log,verbose,command,agent,user,config
+# write = system,call,log,verbose,command,agent,user,config
+# deny=127.0.0.1/255.255.255.255
+
+# Invalid User
+printf "Action: Login\r\nUsername: foo\r\nSecret: moo\r\n\r\n" | nc localhost 5038
+
+# Invalid Secret
+printf "Action: Login\r\nUsername: russell\r\nSecret: moo\r\n\r\n" | nc localhost 5038
+
+# Auth Success
+printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\n" | nc -w 1 localhost 5038
+
+# Failed ACL
+printf "Action: Login\r\nUsername: russell2\r\nSecret: blah123\r\n\r\n" | nc -w 1 localhost 5038
+
+# Request Not Allowed
+printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\nAction: Originate\r\n\r\n" | nc -w 1 localhost 5038
+
+# Request Bad Format
+printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\nAction: FakeActionBLAH\r\n\r\n" | nc -w 1 localhost 5038
+
+# Failed Challenge Response
+printf "Action: Challenge\r\nUsername: russell\r\nAuthType: MD5\r\n\r\nAction: Login\r\nUsername: russell\r\nAuthType: MD5\r\nKey: 00000000\r\n\r\n" | nc localhost 5038
+
+# Session Limit
+printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\n" | nc -w 5 localhost 5038 &
+printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\n" | nc -w 1 localhost 5038
+
diff --git a/tests/test_security_events.c b/tests/test_security_events.c
new file mode 100644
index 000000000..33b40e877
--- /dev/null
+++ b/tests/test_security_events.c
@@ -0,0 +1,625 @@
+/*
+ * 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 Test security event generation
+ *
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+/*** MODULEINFO
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/security_events.h"
+
+static void evt_gen_failed_acl(void);
+static void evt_gen_inval_acct_id(void);
+static void evt_gen_session_limit(void);
+static void evt_gen_mem_limit(void);
+static void evt_gen_load_avg(void);
+static void evt_gen_req_no_support(void);
+static void evt_gen_req_not_allowed(void);
+static void evt_gen_auth_method_not_allowed(void);
+static void evt_gen_req_bad_format(void);
+static void evt_gen_successful_auth(void);
+static void evt_gen_unexpected_addr(void);
+static void evt_gen_chal_resp_failed(void);
+static void evt_gen_inval_password(void);
+
+typedef void (*evt_generator)(void);
+static const evt_generator evt_generators[AST_SECURITY_EVENT_NUM_TYPES] = {
+ [AST_SECURITY_EVENT_FAILED_ACL] = evt_gen_failed_acl,
+ [AST_SECURITY_EVENT_INVAL_ACCT_ID] = evt_gen_inval_acct_id,
+ [AST_SECURITY_EVENT_SESSION_LIMIT] = evt_gen_session_limit,
+ [AST_SECURITY_EVENT_MEM_LIMIT] = evt_gen_mem_limit,
+ [AST_SECURITY_EVENT_LOAD_AVG] = evt_gen_load_avg,
+ [AST_SECURITY_EVENT_REQ_NO_SUPPORT] = evt_gen_req_no_support,
+ [AST_SECURITY_EVENT_REQ_NOT_ALLOWED] = evt_gen_req_not_allowed,
+ [AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED] = evt_gen_auth_method_not_allowed,
+ [AST_SECURITY_EVENT_REQ_BAD_FORMAT] = evt_gen_req_bad_format,
+ [AST_SECURITY_EVENT_SUCCESSFUL_AUTH] = evt_gen_successful_auth,
+ [AST_SECURITY_EVENT_UNEXPECTED_ADDR] = evt_gen_unexpected_addr,
+ [AST_SECURITY_EVENT_CHAL_RESP_FAILED] = evt_gen_chal_resp_failed,
+ [AST_SECURITY_EVENT_INVAL_PASSWORD] = evt_gen_inval_password,
+};
+
+static void evt_gen_failed_acl(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ 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 = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "Username",
+ .common.session_id = "Session123",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+
+ .acl_name = "TEST_ACL",
+ };
+
+ inet_aton("192.168.1.1", &sin_local.sin_addr);
+ sin_local.sin_port = htons(12121);
+
+ inet_aton("192.168.1.2", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(12345);
+
+ ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
+}
+
+static void evt_gen_inval_acct_id(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ 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 = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "FakeUser",
+ .common.session_id = "Session456",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+ };
+
+ inet_aton("10.1.2.3", &sin_local.sin_addr);
+ sin_local.sin_port = htons(4321);
+
+ inet_aton("10.1.2.4", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(1234);
+
+ ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
+}
+
+static void evt_gen_session_limit(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ 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 = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "Jenny",
+ .common.session_id = "8675309",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TLS,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TLS,
+ },
+ };
+
+ inet_aton("10.5.4.3", &sin_local.sin_addr);
+ sin_local.sin_port = htons(4444);
+
+ inet_aton("10.5.4.2", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(3333);
+
+ ast_security_event_report(AST_SEC_EVT(&session_limit));
+}
+
+static void evt_gen_mem_limit(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ struct ast_security_event_mem_limit mem_limit = {
+ .common.event_type = AST_SECURITY_EVENT_MEM_LIMIT,
+ .common.version = AST_SECURITY_EVENT_MEM_LIMIT_VERSION,
+ .common.service = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "Felix",
+ .common.session_id = "Session2604",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+ };
+
+ inet_aton("10.10.10.10", &sin_local.sin_addr);
+ sin_local.sin_port = htons(555);
+
+ inet_aton("10.10.10.12", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(5656);
+
+ ast_security_event_report(AST_SEC_EVT(&mem_limit));
+}
+
+static void evt_gen_load_avg(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ struct ast_security_event_load_avg load_avg = {
+ .common.event_type = AST_SECURITY_EVENT_LOAD_AVG,
+ .common.version = AST_SECURITY_EVENT_LOAD_AVG_VERSION,
+ .common.service = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "GuestAccount",
+ .common.session_id = "XYZ123",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+ };
+
+ inet_aton("10.11.12.13", &sin_local.sin_addr);
+ sin_local.sin_port = htons(9876);
+
+ inet_aton("10.12.11.10", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(9825);
+
+ ast_security_event_report(AST_SEC_EVT(&load_avg));
+}
+
+static void evt_gen_req_no_support(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ struct ast_security_event_req_no_support req_no_support = {
+ .common.event_type = AST_SECURITY_EVENT_REQ_NO_SUPPORT,
+ .common.version = AST_SECURITY_EVENT_REQ_NO_SUPPORT_VERSION,
+ .common.service = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "George",
+ .common.session_id = "asdkl23478289lasdkf",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+
+ .request_type = "MakeMeDinner",
+ };
+
+ inet_aton("10.110.120.130", &sin_local.sin_addr);
+ sin_local.sin_port = htons(9888);
+
+ inet_aton("10.120.110.100", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(9777);
+
+ ast_security_event_report(AST_SEC_EVT(&req_no_support));
+}
+
+static void evt_gen_req_not_allowed(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ 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 = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "George",
+ .common.session_id = "alksdjf023423h4lka0df",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+
+ .request_type = "MakeMeBreakfast",
+ .request_params = "BACONNNN!",
+ };
+
+ inet_aton("10.110.120.130", &sin_local.sin_addr);
+ sin_local.sin_port = htons(9888);
+
+ inet_aton("10.120.110.100", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(9777);
+
+ ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
+}
+
+static void evt_gen_auth_method_not_allowed(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ struct ast_security_event_auth_method_not_allowed auth_method_not_allowed = {
+ .common.event_type = AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED,
+ .common.version = AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED_VERSION,
+ .common.service = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "Bob",
+ .common.session_id = "010101010101",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+
+ .auth_method = "PlainText"
+ };
+
+ inet_aton("10.110.120.135", &sin_local.sin_addr);
+ sin_local.sin_port = htons(8754);
+
+ inet_aton("10.120.110.105", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(8745);
+
+ ast_security_event_report(AST_SEC_EVT(&auth_method_not_allowed));
+}
+
+static void evt_gen_req_bad_format(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ 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 = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "Larry",
+ .common.session_id = "838383fhfhf83hf8h3f8h",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+
+ .request_type = "CheeseBurger",
+ .request_params = "Onions,Swiss,MotorOil",
+ };
+
+ inet_aton("10.110.220.230", &sin_local.sin_addr);
+ sin_local.sin_port = htons(1212);
+
+ inet_aton("10.120.210.200", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(2121);
+
+ ast_security_event_report(AST_SEC_EVT(&req_bad_format));
+}
+
+static void evt_gen_successful_auth(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ 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 = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "ValidUser",
+ .common.session_id = "Session456",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+ };
+
+ inet_aton("10.1.2.3", &sin_local.sin_addr);
+ sin_local.sin_port = htons(4321);
+
+ inet_aton("10.1.2.4", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(1234);
+
+ ast_security_event_report(AST_SEC_EVT(&successful_auth));
+}
+
+static void evt_gen_unexpected_addr(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_expected = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ struct ast_security_event_unexpected_addr unexpected_addr = {
+ .common.event_type = AST_SECURITY_EVENT_UNEXPECTED_ADDR,
+ .common.version = AST_SECURITY_EVENT_UNEXPECTED_ADDR_VERSION,
+ .common.service = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "CoolUser",
+ .common.session_id = "Session789",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+
+ .expected_addr = {
+ .sin = &sin_expected,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_UDP,
+ },
+ };
+
+ inet_aton("10.1.2.3", &sin_local.sin_addr);
+ sin_local.sin_port = htons(4321);
+
+ inet_aton("10.1.2.4", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(1234);
+
+ inet_aton("10.1.2.5", &sin_expected.sin_addr);
+ sin_expected.sin_port = htons(2343);
+
+ ast_security_event_report(AST_SEC_EVT(&unexpected_addr));
+}
+
+static void evt_gen_chal_resp_failed(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ 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 = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "SuperDuperUser",
+ .common.session_id = "Session1231231231",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+
+ .challenge = "8adf8a9sd8fas9df23ljk4",
+ .response = "9u3jlaksdjflakjsdfoi23",
+ .expected_response = "oiafaljhadf9834luahk3k",
+ };
+
+ inet_aton("10.1.2.3", &sin_local.sin_addr);
+ sin_local.sin_port = htons(4321);
+
+ inet_aton("10.1.2.4", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(1234);
+
+ ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
+}
+
+static void evt_gen_inval_password(void)
+{
+ struct sockaddr_in sin_local = {
+ .sin_family = AF_INET
+ };
+ struct sockaddr_in sin_remote = {
+ .sin_family = AF_INET
+ };
+ struct timeval session_tv = ast_tvnow();
+ 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 = "TEST",
+ .common.module = AST_MODULE,
+ .common.account_id = "AccountIDGoesHere",
+ .common.session_id = "SessionIDGoesHere",
+ .common.session_tv = &session_tv,
+ .common.local_addr = {
+ .sin = &sin_local,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+ .common.remote_addr = {
+ .sin = &sin_remote,
+ .transport = AST_SECURITY_EVENT_TRANSPORT_TCP,
+ },
+ };
+
+ inet_aton("10.200.100.30", &sin_local.sin_addr);
+ sin_local.sin_port = htons(4321);
+
+ inet_aton("10.200.100.40", &sin_remote.sin_addr);
+ sin_remote.sin_port = htons(1234);
+
+ ast_security_event_report(AST_SEC_EVT(&inval_password));
+}
+
+static void gen_events(struct ast_cli_args *a)
+{
+ unsigned int i;
+
+ ast_cli(a->fd, "Generating some security events ...\n");
+
+ for (i = 0; i < ARRAY_LEN(evt_generators); i++) {
+ const char *event_type = ast_security_event_get_name(i);
+
+ if (!evt_generators[i]) {
+ ast_cli(a->fd, "*** No event generator for event type '%s' ***\n",
+ event_type);
+ continue;
+ }
+
+ ast_cli(a->fd, "Generating a '%s' security event ...\n", event_type);
+
+ evt_generators[i]();
+ }
+
+ ast_cli(a->fd, "Security event generation complete.\n");
+}
+
+static char *handle_cli_sec_evt_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "securityevents test generation";
+ e->usage = ""
+ "Usage: securityevents test generation"
+ "";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ case CLI_HANDLER:
+ gen_events(a);
+ return CLI_SUCCESS;
+ }
+
+ return CLI_FAILURE;
+}
+
+static struct ast_cli_entry cli_sec_evt[] = {
+ AST_CLI_DEFINE(handle_cli_sec_evt_test, "Test security event generation"),
+};
+
+static int unload_module(void)
+{
+ return ast_cli_unregister_multiple(cli_sec_evt, ARRAY_LEN(cli_sec_evt));
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_cli_register_multiple(cli_sec_evt, ARRAY_LEN(cli_sec_evt));
+
+ return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Test Security Event Generation");