Add an API for reporting security events, and a security event logging module.
authorRussell Bryant <russell@russellbryant.com>
Sat, 11 Jul 2009 19:15:03 +0000 (19:15 +0000)
committerRussell Bryant <russell@russellbryant.com>
Sat, 11 Jul 2009 19:15:03 +0000 (19:15 +0000)
This commit introduces the security events API.  This API is to be used by
Asterisk components to report events that have security implications.
A simple example is when a connection is made but fails authentication.  These
events can be used by external tools manipulate firewall rules or something
similar after detecting unusual activity based on security events.

Inside of Asterisk, the events go through the ast_event API.  This means that
they have a binary encoding, and it is easy to write code to subscribe to these
events and do something with them.

One module is provided that is a subscriber to these events - res_security_log.
This module turns security events into a parseable text format and sends them
to the "security" logger level.  Using logger.conf, these log entries may be
sent to a file, or to syslog.

One service, AMI, has been fully updated for reporting security events.
AMI was chosen as it was a fairly straight forward service to convert.
The next target will be chan_sip.  That will be more complicated and will
be done as its own project as the next phase of security events work.

For more information on the security events framework, see the documentation
generated from doc/tex/.  "make asterisk.pdf"

Review: https://reviewboard.asterisk.org/r/273/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@206021 65c4cc65-6c06-0410-ace0-fbb531ad65f3

doc/tex/asterisk.tex
doc/tex/security-events.tex [new file with mode: 0644]
include/asterisk/event_defs.h
include/asterisk/security_events.h [new file with mode: 0644]
include/asterisk/security_events_defs.h [new file with mode: 0644]
main/event.c
main/manager.c
main/security_events.c [new file with mode: 0644]
res/res_security_log.c [new file with mode: 0644]
tests/test_ami_security_events.sh [new file with mode: 0755]
tests/test_security_events.c [new file with mode: 0644]

index 3900f4c..935d050 100644 (file)
@@ -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 (file)
index 0000000..d46b150
--- /dev/null
@@ -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}
+
index 99edb6f..3779dac 100644 (file)
@@ -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 (file)
index 0000000..c15d04f
--- /dev/null
@@ -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 (file)
index 0000000..e39cf31
--- /dev/null
@@ -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__ */
index 6a70a67..44c6571 100644 (file)
@@ -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;
        }
 
index 0670da3..c7e4ce4 100644 (file)
@@ -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 (file)
index 0000000..d1e2ac0
--- /dev/null
@@ -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 (file)
index 0000000..2e3d4af
--- /dev/null
@@ -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 (executable)
index 0000000..6f125dc
--- /dev/null
@@ -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 (file)
index 0000000..33b40e8
--- /dev/null
@@ -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");