diff --git a/doc/tex/asterisk.tex b/doc/tex/asterisk.tex index 3900f4cb6b215b5d113d1378f1905207361414ae..935d0506a603778bf3b7d8e622a97264c8b6c411 100644 --- a/doc/tex/asterisk.tex +++ b/doc/tex/asterisk.tex @@ -144,6 +144,9 @@ reference purposes. \chapter{Calendaring} \input{calendaring.tex} +\chapter{Security Framework} + \input{security-events.tex} + \chapter{Development} \section{Backtrace} \input{backtrace.tex} diff --git a/doc/tex/security-events.tex b/doc/tex/security-events.tex new file mode 100644 index 0000000000000000000000000000000000000000..d46b1505d73ffb5c200054e287d6dec016a3a93a --- /dev/null +++ b/doc/tex/security-events.tex @@ -0,0 +1,250 @@ +\section{Introduction} + + Attacks on Voice over IP networks are becoming increasingly more common. It +has become clear that we must do something within Asterisk to help mitigate +these attacks. + + Through a number of discussions with groups of developers in the Asterisk +community, the general consensus is that the best thing that we can do within +Asterisk is to build a framework which recognizes and reports events that could +potentially have security implications. Each channel driver has a different +concept of what is an "event", and then each administrator has different +thresholds of what is a "bad" event and what is a restorative event. The +process of acting upon this information is left to an external program to +correlate and then take action - block traffic, modify dialing rules, etc. It +was decided that embedding actions inside of Asterisk was inappropriate, as the +complexity of construction of such rule sets is difficult and there was no +agreement on where rules should be enabled or how they should be processed. The +addition of a major section of code to handle rule expiration and severity +interpretation was significant. As a final determining factor, there are +external programs and services which already parse log files and act in concert +with packet filters or external devices to protect or alter network security +models for IP connected hosts. + +\section{Framework Overview} + + This section discusses the architecture of the Asterisk modifications being +proposed. + + There are two main components that we propose for the initial +implementation of the security framework: + +\begin{itemize} + \item Security Event Generation + \item Security Event Logger +\end{itemize} + +\subsection{Security Event Generation} + + The ast\_event API is used for the generation of security events. That +way, the events are in an easily interpretable format within Asterisk to +make it easy to write modules that do things with them. There are also some +helper data structures and functions to aid Asterisk modules in reporting these +security events with the proper contents. + + The next section of this document contains the current list of security events +being proposed. Each security event type has some required pieces of +information and some other optional pieces of information. + + Subscribing to security events from within Asterisk can be done by +subscribing to events of type AST\_EVENT\_SECURITY. These events have an +information element, AST\_EVENT\_IE\_SECURITY\_EVENT, which identifies the security +event sub-type (from the list described in the next section). The result of the +information elements in the events contain the required and optional meta data +associated with the event sub-type. + +\subsection{Security Event Logger} + + In addition to the infrastructure for generating the events, one module that +is a consumer of these events has been implemented. + + Asterisk trunk was recently updated to include support for dynamic logger +levels. This module takes advantage of this functionality to create a +custom "security" logger level. Then, when this module is in use, logger.conf +can be configured to put security events into a file: + +\begin{verbatim} + security_log => security +\end{verbatim} + + The content of this file is a well defined and easily interpretable +format for external scripts to read and act upon. The definition for the format +of the log file is described later in this chapter. + +\section{Events to Log} + +\begin{verbatim} +(-) required +(+) optional + +Invalid Account ID + (-) Local address family/IP address/port/transport + (-) Remote address family/IP address/port/transport + (-) Service (SIP, AMI, IAX2, ...) + (-) System Name + (+) Module + (+) Account ID (username, etc) + (+) Session ID (CallID, etc) + (+) Session timestamp (required if Session ID present) + (-) Event timestamp (sub-second precision) + +Failed ACL match + -> everything from invalid account ID + (+) Name of ACL (when we have named ACLs) + +Invalid Challenge/Response + -> everything from invalid account ID + (-) Challenge + (-) Response + (-) Expected Response + +Invalid Password + -> everything from invalid account ID + +Successful Authentication + -> informational event + -> everything from invalid account ID + +Invalid formatting of Request + -> everything from invalid account ID + -> account ID optional + (-) Request Type + (+) Request parameters + +Session Limit Reached (such as a call limit) + -> everything from invalid account ID + +Memory Limit Reached + -> everything from invalid account ID + +Maximum Load Average Reached + -> everything from invalid account ID + +Request Not Allowed + -> everything from invalid account ID + (-) Request Type + (+) Request parameters + +Request Not Supported + -> everything from invalid account ID + (-) Request Type + +Authentication Method Not Allowed + -> everything from invalid account ID + (-) Authentication Method attempted + +In dialog message from unexpected host + -> everything from invalid account ID + (-) expected host +\end{verbatim} + +\section{Security Log File Format} + + The beginning of each line in the log file is the same as it is for other +logger levels within Asterisk. + +\begin{verbatim} + [Feb 11 07:57:03] SECURITY[23736] res_security_log.c: <...> +\end{verbatim} + + The part of the log entry identified by <...> is where the security event +content resides. The security event content is a comma separated list +of key value pairs. The key is the information element type, and the value is a +quoted string that contains the associated meta data for that information +element. Any embedded quotes within the content are escaped with a +backslash. + +\begin{verbatim} + INFORMATION_ELEMENT_1="IE1 content",INFORMATION_ELEMENT_2="IE2 content" +\end{verbatim} + +The following table includes potential information elements and what the +associated content looks like: + +\begin{verbatim} +IE: SecurityEvent +Content: This is the security event sub-type. +Values: FailedACL, InvalidAccountID, SessionLimit, MemoryLimit, LoadAverageLimit, + RequestNotSupported, RequestNotAllowed, AuthMethodNotAllowed, + ReqBadFormat, UnexpectedAddress, ChallengeResponseFailed, + InvalidPassword + +IE: EventVersion +Content: This is a numeric value that indicates when updates are made to the + content of the event. +Values: Monotonically increasing integer, starting at 1 + +IE: Service +Content: This is the Asterisk service that generated the event. +Values: TEST, SIP, AMI + +IE: Module +Content: This is the Asterisk module that generated the event. +Values: chan_sip + +IE: AccountID +Content: This is a string used to identify the account associated with the + event. In most cases, this would be a username. + +IE: SessionID +Content: This is a string used to identify the session associated with the + event. The format of the session identifier is specific to the + service. In the case of SIP, this would be the Call-ID. + +IE: SessionTV +Content: The time that the session associated with the SessionID started. +Values: <seconds>-<microseconds> since epoch + +IE: ACLName +Content: This is a string that identifies which named ACL is associated with + this event. + +IE: LocalAddress +Content: This is the local address that was contacted for the related event. +Values: <Address Family>/<Transport>/<Address>/<Port> +Examples: + -> IPV4/UDP/192.168.1.1/5060 + -> IPV4/TCP/192.168.1.1/5038 + +IE: RemoteAddress +Content: This is the remote address associated with the event. +Examples: + -> IPV4/UDP/192.168.1.2/5060 + -> IPV4/TCP/192.168.1.2/5038 + +IE: ExpectedAddress +Content: This is the address that was expected to be the remote address. +Examples: + -> IPV4/UDP/192.168.1.2/5060 + -> IPV4/TCP/192.168.1.2/5038 + +IE: EventTV +Content: This is the timestamp of when the event occurred. +Values: <seconds>-<microseconds> since epoch + +IE: RequestType +Content: This is a service specific string that represents the invalid request + +IE: RequestParams +Content: This is a service specific string that represents relevant parameters + given with a request that was considered invalid. + +IE: AuthMethod +Content: This is a service specific string that represents an authentication + method that was used or requested. + +IE: Challenge +Content: This is a service specific string that represents the challenge + provided to a user attempting challenge/response authentication. + +IE: Response +Content: This is a service specific string that represents the response + received from a user attempting challenge/response authentication. + +IE: ExpectedResponse +Content: This is a service specific string that represents the response + that was expected to be received from a user attempting + challenge/response authentication. + +\end{verbatim} + diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h index 99edb6f5599bdfd754fa1380a2753b355dc93655..3779dac73cbe1e05b79ce3a6c4777da90a650b91 100644 --- a/include/asterisk/event_defs.h +++ b/include/asterisk/event_defs.h @@ -49,58 +49,60 @@ enum ast_event_type { * directly, in general. Use AST_EVENT_DEVICE_STATE instead. */ AST_EVENT_DEVICE_STATE_CHANGE = 0x06, /*! Channel Event Logging events */ - AST_EVENT_CEL = 0x07, + AST_EVENT_CEL = 0x07, + /*! A report of a security related event (see security_events.h) */ + AST_EVENT_SECURITY = 0x08, /*! Number of event types. This should be the last event type + 1 */ - AST_EVENT_TOTAL = 0x08, + AST_EVENT_TOTAL = 0x09, }; /*! \brief Event Information Element types */ enum ast_event_ie_type { /*! Used to terminate the arguments to event functions */ - AST_EVENT_IE_END = -1, + AST_EVENT_IE_END = -1, /*! * \brief Number of new messages * Used by: AST_EVENT_MWI * Payload type: UINT */ - AST_EVENT_IE_NEWMSGS = 0x01, + AST_EVENT_IE_NEWMSGS = 0x0001, /*! * \brief Number of * Used by: AST_EVENT_MWI * Payload type: UINT */ - AST_EVENT_IE_OLDMSGS = 0x02, + AST_EVENT_IE_OLDMSGS = 0x0002, /*! * \brief Mailbox name \verbatim (mailbox[@context]) \endverbatim * Used by: AST_EVENT_MWI * Payload type: STR */ - AST_EVENT_IE_MAILBOX = 0x03, + AST_EVENT_IE_MAILBOX = 0x0003, /*! * \brief Unique ID * Used by: AST_EVENT_SUB, AST_EVENT_UNSUB * Payload type: UINT */ - AST_EVENT_IE_UNIQUEID = 0x04, + AST_EVENT_IE_UNIQUEID = 0x0004, /*! * \brief Event type * Used by: AST_EVENT_SUB, AST_EVENT_UNSUB * Payload type: UINT */ - AST_EVENT_IE_EVENTTYPE = 0x05, + AST_EVENT_IE_EVENTTYPE = 0x0005, /*! * \brief Hint that someone cares that an IE exists * Used by: AST_EVENT_SUB * Payload type: UINT (ast_event_ie_type) */ - AST_EVENT_IE_EXISTS = 0x6, + AST_EVENT_IE_EXISTS = 0x0006, /*! * \brief Device Name * Used by AST_EVENT_DEVICE_STATE_CHANGE * Payload type: STR */ - AST_EVENT_IE_DEVICE = 0x07, + AST_EVENT_IE_DEVICE = 0x0007, /*! * \brief Generic State IE * Used by AST_EVENT_DEVICE_STATE_CHANGE @@ -108,162 +110,181 @@ enum ast_event_ie_type { * The actual state values depend on the event which * this IE is a part of. */ - AST_EVENT_IE_STATE = 0x08, + AST_EVENT_IE_STATE = 0x0008, /*! * \brief Context IE * Used by AST_EVENT_MWI * Payload type: str */ - AST_EVENT_IE_CONTEXT = 0x09, + AST_EVENT_IE_CONTEXT = 0x0009, /*! * \brief Channel Event Type * Used by: AST_EVENT_CEL * Payload type: UINT */ - AST_EVENT_IE_CEL_EVENT_TYPE = 0x0a, + AST_EVENT_IE_CEL_EVENT_TYPE = 0x000a, /*! * \brief Channel Event Time (seconds) * Used by: AST_EVENT_CEL * Payload type: UINT */ - AST_EVENT_IE_CEL_EVENT_TIME = 0x0b, + AST_EVENT_IE_CEL_EVENT_TIME = 0x000b, /*! * \brief Channel Event Time (micro-seconds) * Used by: AST_EVENT_CEL * Payload type: UINT */ - AST_EVENT_IE_CEL_EVENT_TIME_USEC = 0x0c, + AST_EVENT_IE_CEL_EVENT_TIME_USEC = 0x000c, /*! * \brief Channel Event User Event Name * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_USEREVENT_NAME = 0x0d, + AST_EVENT_IE_CEL_USEREVENT_NAME = 0x000d, /*! * \brief Channel Event CID name * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_CIDNAME = 0x0e, + AST_EVENT_IE_CEL_CIDNAME = 0x000e, /*! * \brief Channel Event CID num * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_CIDNUM = 0x0f, + AST_EVENT_IE_CEL_CIDNUM = 0x000f, /*! * \brief Channel Event extension name * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_EXTEN = 0x10, + AST_EVENT_IE_CEL_EXTEN = 0x0010, /*! * \brief Channel Event context name * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_CONTEXT = 0x11, + AST_EVENT_IE_CEL_CONTEXT = 0x0011, /*! * \brief Channel Event channel name * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_CHANNAME = 0x12, + AST_EVENT_IE_CEL_CHANNAME = 0x0012, /*! * \brief Channel Event app name * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_APPNAME = 0x13, + AST_EVENT_IE_CEL_APPNAME = 0x0013, /*! * \brief Channel Event app args/data * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_APPDATA = 0x14, + AST_EVENT_IE_CEL_APPDATA = 0x0014, /*! * \brief Channel Event AMA flags * Used by: AST_EVENT_CEL * Payload type: UINT */ - AST_EVENT_IE_CEL_AMAFLAGS = 0x15, + AST_EVENT_IE_CEL_AMAFLAGS = 0x0015, /*! * \brief Channel Event AccountCode * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_ACCTCODE = 0x16, + AST_EVENT_IE_CEL_ACCTCODE = 0x0016, /*! * \brief Channel Event UniqueID * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_UNIQUEID = 0x17, + AST_EVENT_IE_CEL_UNIQUEID = 0x0017, /*! * \brief Channel Event Userfield * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_USERFIELD = 0x18, + AST_EVENT_IE_CEL_USERFIELD = 0x0018, /*! * \brief Channel Event CID ANI field * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_CIDANI = 0x19, + AST_EVENT_IE_CEL_CIDANI = 0x0019, /*! * \brief Channel Event CID RDNIS field * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_CIDRDNIS = 0x1a, + AST_EVENT_IE_CEL_CIDRDNIS = 0x001a, /*! * \brief Channel Event CID dnid * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_CIDDNID = 0x1b, + AST_EVENT_IE_CEL_CIDDNID = 0x001b, /*! * \brief Channel Event Peer -- for Things involving multiple channels, like BRIDGE * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_PEER = 0x1c, + AST_EVENT_IE_CEL_PEER = 0x001c, /*! * \brief Channel Event LinkedID * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_LINKEDID = 0x1d, + AST_EVENT_IE_CEL_LINKEDID = 0x001d, /*! * \brief Channel Event peeraccount * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_PEERACCT = 0x1e, + AST_EVENT_IE_CEL_PEERACCT = 0x001e, /*! * \brief Channel Event extra data * Used by: AST_EVENT_CEL * Payload type: STR */ - AST_EVENT_IE_CEL_EXTRA = 0x1f, + AST_EVENT_IE_CEL_EXTRA = 0x001f, /*! * \brief Description * Used by: AST_EVENT_SUB, AST_EVENT_UNSUB * Payload type: STR */ - AST_EVENT_IE_DESCRIPTION = 0x20, + AST_EVENT_IE_DESCRIPTION = 0x0020, /*! * \brief Entity ID * Used by All events * Payload type: RAW * This IE indicates which server the event originated from */ - AST_EVENT_IE_EID = 0x21, + AST_EVENT_IE_EID = 0x0021, + AST_EVENT_IE_SECURITY_EVENT = 0x0022, + AST_EVENT_IE_EVENT_VERSION = 0x0023, + AST_EVENT_IE_SERVICE = 0x0024, + AST_EVENT_IE_MODULE = 0x0025, + AST_EVENT_IE_ACCOUNT_ID = 0x0026, + AST_EVENT_IE_SESSION_ID = 0x0027, + AST_EVENT_IE_SESSION_TV = 0x0028, + AST_EVENT_IE_ACL_NAME = 0x0029, + AST_EVENT_IE_LOCAL_ADDR = 0x002a, + AST_EVENT_IE_REMOTE_ADDR = 0x002b, + AST_EVENT_IE_EVENT_TV = 0x002c, + AST_EVENT_IE_REQUEST_TYPE = 0x002d, + AST_EVENT_IE_REQUEST_PARAMS = 0x002e, + AST_EVENT_IE_AUTH_METHOD = 0x002f, + AST_EVENT_IE_SEVERITY = 0x0030, + AST_EVENT_IE_EXPECTED_ADDR = 0x0031, + AST_EVENT_IE_CHALLENGE = 0x0032, + AST_EVENT_IE_RESPONSE = 0x0033, + AST_EVENT_IE_EXPECTED_RESPONSE = 0x0034, + /*! \brief Must be the last IE value +1 */ + AST_EVENT_IE_TOTAL = 0x0035, }; -#define AST_EVENT_IE_MAX AST_EVENT_IE_EID - /*! * \brief Payload types for event information elements */ diff --git a/include/asterisk/security_events.h b/include/asterisk/security_events.h new file mode 100644 index 0000000000000000000000000000000000000000..c15d04f0e0e8a6f20b029a35b68ac6912419d5bd --- /dev/null +++ b/include/asterisk/security_events.h @@ -0,0 +1,114 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009, Digium, Inc. + * + * Russell Bryant <russell@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * + * \brief Security Event Reporting API + * + * \author Russell Bryant <russell@digium.com> + */ + +#ifndef __AST_SECURITY_EVENTS_H__ +#define __AST_SECURITY_EVENTS_H__ + +#include "asterisk/event.h" + +/* Data structure definitions */ +#include "asterisk/security_events_defs.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/*! + * \brief Report a security event + * + * \param[in] sec security event data. Callers of this function should never + * declare an instance of ast_security_event_common directly. The + * argument should be an instance of a specific security event + * descriptor which has ast_security_event_common at the very + * beginning. + * + * \retval 0 success + * \retval non-zero failure + */ +int ast_security_event_report(const struct ast_security_event_common *sec); + +struct ast_security_event_ie_type { + enum ast_event_ie_type ie_type; + /*! \brief For internal usage */ + size_t offset; +}; + +/*! + * \brief Get the list of required IEs for a given security event sub-type + * + * \param[in] event_type security event sub-type + * + * \retval NULL invalid event_type + * \retval non-NULL An array terminated with the value AST_EVENT_IE_END + * + * \since 1.6.3 + */ +const struct ast_security_event_ie_type *ast_security_event_get_required_ies( + const enum ast_security_event_type event_type); + +/*! + * \brief Get the list of optional IEs for a given security event sub-type + * + * \param[in] event_type security event sub-type + * + * \retval NULL invalid event_type + * \retval non-NULL An array terminated with the value AST_EVENT_IE_END + * + * \since 1.6.3 + */ +const struct ast_security_event_ie_type *ast_security_event_get_optional_ies( + const enum ast_security_event_type event_type); + +/*! + * \brief Get the name of a security event sub-type + * + * \param[in] event_type security event sub-type + * + * \retval NULL if event_type is invalid + * \retval non-NULL the name of the security event type + * + * \since 1.6.3 + */ +const char *ast_security_event_get_name(const enum ast_security_event_type event_type); + +/*! + * \brief Get the name of a security event severity + * + * \param[in] severity security event severity + * + * \retval NULL if severity is invalid + * \retval non-NULL the name of the security event severity + * + * \since 1.6.3 + */ +const char *ast_security_event_severity_get_name( + const enum ast_security_event_severity severity); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* __AST_SECURITY_EVENTS_H__ */ diff --git a/include/asterisk/security_events_defs.h b/include/asterisk/security_events_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..e39cf312d79291cd98b22946b595af9d911d61c5 --- /dev/null +++ b/include/asterisk/security_events_defs.h @@ -0,0 +1,470 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009, Digium, Inc. + * + * Russell Bryant <russell@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * + * \brief Security Event Reporting Data Structures + * + * \author Russell Bryant <russell@digium.com> + */ + +#ifndef __AST_SECURITY_EVENTS_DEFS_H__ +#define __AST_SECURITY_EVENTS_DEFS_H__ + +#include "asterisk/network.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/*! + * \brief Security event types + * + * AST_EVENT_SECURITY is the event type of an ast_event generated as a security + * event. The event will have an information element of type + * AST_EVENT_IE_SECURITY_EVENT which identifies the security event sub-type. + * This enum defines the possible values for this sub-type. + */ +enum ast_security_event_type { + /*! + * \brief Failed ACL + * + * This security event should be generated when an incoming request + * was made, but was denied due to configured IP address access control + * lists. + */ + AST_SECURITY_EVENT_FAILED_ACL, + /*! + * \brief Invalid Account ID + * + * This event is used when an invalid account identifier is supplied + * during authentication. For example, if an invalid username is given, + * this event should be used. + */ + AST_SECURITY_EVENT_INVAL_ACCT_ID, + /*! + * \brief Session limit reached + * + * A request has been denied because a configured session limit has been + * reached, such as a call limit. + */ + AST_SECURITY_EVENT_SESSION_LIMIT, + /*! + * \brief Memory limit reached + * + * A request has been denied because a configured memory limit has been + * reached. + */ + AST_SECURITY_EVENT_MEM_LIMIT, + /*! + * \brief Load Average limit reached + * + * A request has been denied because a configured load average limit has been + * reached. + */ + AST_SECURITY_EVENT_LOAD_AVG, + /*! + * \brief A request was made that we understand, but do not support + */ + AST_SECURITY_EVENT_REQ_NO_SUPPORT, + /*! + * \brief A request was made that is not allowed + */ + AST_SECURITY_EVENT_REQ_NOT_ALLOWED, + /*! + * \brief The attempted authentication method is not allowed + */ + AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED, + /*! + * \brief Request received with bad formatting + */ + AST_SECURITY_EVENT_REQ_BAD_FORMAT, + /*! + * \brief FYI FWIW, Successful authentication has occurred + */ + AST_SECURITY_EVENT_SUCCESSFUL_AUTH, + /*! + * \brief An unexpected source address was seen for a session in progress + */ + AST_SECURITY_EVENT_UNEXPECTED_ADDR, + /*! + * \brief An attempt at challenge/response authentication failed + */ + AST_SECURITY_EVENT_CHAL_RESP_FAILED, + /*! + * \brief An attempt at basic password authentication failed + */ + AST_SECURITY_EVENT_INVAL_PASSWORD, + /* \brief This _must_ stay at the end. */ + AST_SECURITY_EVENT_NUM_TYPES +}; + +/*! + * \brief the severity of a security event + * + * This is defined as a bit field to make it easy for consumers of the API to + * subscribe to any combination of the defined severity levels. + * + * XXX \todo Do we need any more levels here? + */ +enum ast_security_event_severity { + /*! \brief Informational event, not something that has gone wrong */ + AST_SECURITY_EVENT_SEVERITY_INFO = (1 << 0), + /*! \brief Something has gone wrong */ + AST_SECURITY_EVENT_SEVERITY_ERROR = (1 << 1), +}; + +/*! + * \brief Transport types + */ +enum ast_security_event_transport_type { + AST_SECURITY_EVENT_TRANSPORT_UDP, + AST_SECURITY_EVENT_TRANSPORT_TCP, + AST_SECURITY_EVENT_TRANSPORT_TLS, +}; + +#define AST_SEC_EVT(e) ((struct ast_security_event_common *) e) + +struct ast_security_event_ipv4_addr { + const struct sockaddr_in *sin; + enum ast_security_event_transport_type transport; +}; + +/*! + * \brief Common structure elements + * + * This is the structure header for all event descriptor structures defined + * below. The contents of this structure are very important and must not + * change. Even though these structures are exposed via a public API, we have + * a version field that can be used to ensure ABI safety. If the event + * descriptors need to be changed or updated in the future, we can safely do + * so and can detect ABI changes at runtime. + */ +struct ast_security_event_common { + /*! \brief The security event sub-type */ + enum ast_security_event_type event_type; + /*! \brief security event version */ + uint32_t version; + /*! + * \brief Service that generated the event + * \note Always required + * + * Examples: "SIP", "AMI" + */ + const char *service; + /*! + * \brief Module, Normally the AST_MODULE define + * \note Always optional + */ + const char *module; + /*! + * \brief Account ID, specific to the service type + * \note optional/required, depending on event type + */ + const char *account_id; + /*! + * \brief Session ID, specific to the service type + * \note Always required + */ + const char *session_id; + /*! + * \brief Session timeval, when the session started + * \note Always optional + */ + const struct timeval *session_tv; + /*! + * \brief Local address the request came in on + * \note Always required + */ + struct ast_security_event_ipv4_addr local_addr; + /*! + * \brief Remote address the request came from + * \note Always required + */ + struct ast_security_event_ipv4_addr remote_addr; +}; + +/*! + * \brief Checking against an IP access control list failed + */ +struct ast_security_event_failed_acl { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_FAILED_ACL_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; + /*! + * \brief ACL name, identifies which ACL was hit + * \note optional + */ + const char *acl_name; +}; + +/*! + * \brief Invalid account ID specified (invalid username, for example) + */ +struct ast_security_event_inval_acct_id { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; +}; + +/*! + * \brief Request denied because of a session limit + */ +struct ast_security_event_session_limit { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_SESSION_LIMIT_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; +}; + +/*! + * \brief Request denied because of a memory limit + */ +struct ast_security_event_mem_limit { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_MEM_LIMIT_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; +}; + +/*! + * \brief Request denied because of a load average limit + */ +struct ast_security_event_load_avg { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_LOAD_AVG_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; +}; + +/*! + * \brief Request denied because we don't support it + */ +struct ast_security_event_req_no_support { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_REQ_NO_SUPPORT_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; + /*! + * \brief Request type that was made + * \note required + */ + const char *request_type; +}; + +/*! + * \brief Request denied because it's not allowed + */ +struct ast_security_event_req_not_allowed { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; + /*! + * \brief Request type that was made + * \note required + */ + const char *request_type; + /*! + * \brief Request type that was made + * \note optional + */ + const char *request_params; +}; + +/*! + * \brief Auth method used not allowed + */ +struct ast_security_event_auth_method_not_allowed { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; + /*! + * \brief Auth method attempted + * \note required + */ + const char *auth_method; +}; + +/*! + * \brief Invalid formatting of request + */ +struct ast_security_event_req_bad_format { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID optional + */ + struct ast_security_event_common common; + /*! + * \brief Request type that was made + * \note required + */ + const char *request_type; + /*! + * \brief Request type that was made + * \note optional + */ + const char *request_params; +}; + +/*! + * \brief Successful authentication + */ +struct ast_security_event_successful_auth { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; +}; + +/*! + * \brief Unexpected source address for a session in progress + */ +struct ast_security_event_unexpected_addr { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_UNEXPECTED_ADDR_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; + /*! + * \brief Expected remote address + * \note required + */ + struct ast_security_event_ipv4_addr expected_addr; +}; + +/*! + * \brief An attempt at challenge/response auth failed + */ +struct ast_security_event_chal_resp_failed { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; + /*! + * \brief Challenge provided + * \note required + */ + const char *challenge; + /*! + * \brief Response received + * \note required + */ + const char *response; + /*! + * \brief Response expected to be received + * \note required + */ + const char *expected_response; +}; + +/*! + * \brief An attempt at basic password auth failed + */ +struct ast_security_event_inval_password { + /*! + * \brief Event descriptor version + * \note This _must_ be changed if this event descriptor is changed. + */ + #define AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION 1 + /*! + * \brief Common security event descriptor elements + * \note Account ID required + */ + struct ast_security_event_common common; +}; + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* __AST_SECURITY_EVENTS_DEFS_H__ */ diff --git a/main/event.c b/main/event.c index 6a70a674184e7d37f773ebd9c2e58ca48c8a5080..44c65714d3cc2c384a14effc4d7897bd3327a57b 100644 --- a/main/event.c +++ b/main/event.c @@ -183,63 +183,77 @@ static struct { }; /*! - * The index of each entry _must_ match the event type number! + * \brief Event Names */ -static struct event_name { - enum ast_event_type type; - const char *name; -} event_names[] = { - { 0, "" }, - { AST_EVENT_CUSTOM, "Custom" }, - { AST_EVENT_MWI, "MWI" }, - { AST_EVENT_SUB, "Subscription" }, - { AST_EVENT_UNSUB, "Unsubscription" }, - { AST_EVENT_DEVICE_STATE, "DeviceState" }, - { AST_EVENT_DEVICE_STATE_CHANGE, "DeviceStateChange" }, - { AST_EVENT_CEL, "CEL" }, +static const char * const event_names[AST_EVENT_TOTAL] = { + [AST_EVENT_CUSTOM] = "Custom", + [AST_EVENT_MWI] = "MWI", + [AST_EVENT_SUB] = "Subscription", + [AST_EVENT_UNSUB] = "Unsubscription", + [AST_EVENT_DEVICE_STATE] = "DeviceState", + [AST_EVENT_DEVICE_STATE_CHANGE] = "DeviceStateChange", + [AST_EVENT_CEL] = "CEL", + [AST_EVENT_SECURITY] = "Security", }; /*! - * The index of each entry _must_ match the event ie number! + * \brief IE payload types and names */ -static struct ie_map { - enum ast_event_ie_type ie_type; +static const struct ie_map { enum ast_event_ie_pltype ie_pltype; const char *name; -} ie_maps[] = { - { 0, 0, "" }, - { AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, "NewMessages" }, - { AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, "OldMessages" }, - { AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, "Mailbox" }, - { AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, "UniqueID" }, - { AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, "EventType" }, - { AST_EVENT_IE_EXISTS, AST_EVENT_IE_PLTYPE_UINT, "Exists" }, - { AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "Device" }, - { AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, "State" }, - { AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "Context" }, - { AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, "EntityID" }, - { AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, "CELEventType" }, - { AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, "CELEventTime" }, - { AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, "CELEventTimeUSec" }, - { AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_UINT, "CELUserEventName" }, - { AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, "CELCIDName" }, - { AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, "CELCIDNum" }, - { AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, "CELExten" }, - { AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "CELContext" }, - { AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, "CELChanName" }, - { AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, "CELAppName" }, - { AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, "CELAppData" }, - { AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_STR, "CELAMAFlags" }, - { AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_UINT, "CELAcctCode" }, - { AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, "CELUniqueID" }, - { AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, "CELUserField" }, - { AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, "CELCIDani" }, - { AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, "CELCIDrdnis" }, - { AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, "CELCIDdnid" }, - { AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, "CELPeer" }, - { AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, "CELLinkedID" }, - { AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, "CELPeerAcct" }, - { AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, "CELExtra" }, +} ie_maps[AST_EVENT_IE_TOTAL] = { + [AST_EVENT_IE_NEWMSGS] = { AST_EVENT_IE_PLTYPE_UINT, "NewMessages" }, + [AST_EVENT_IE_OLDMSGS] = { AST_EVENT_IE_PLTYPE_UINT, "OldMessages" }, + [AST_EVENT_IE_MAILBOX] = { AST_EVENT_IE_PLTYPE_STR, "Mailbox" }, + [AST_EVENT_IE_UNIQUEID] = { AST_EVENT_IE_PLTYPE_UINT, "UniqueID" }, + [AST_EVENT_IE_EVENTTYPE] = { AST_EVENT_IE_PLTYPE_UINT, "EventType" }, + [AST_EVENT_IE_EXISTS] = { AST_EVENT_IE_PLTYPE_UINT, "Exists" }, + [AST_EVENT_IE_DEVICE] = { AST_EVENT_IE_PLTYPE_STR, "Device" }, + [AST_EVENT_IE_STATE] = { AST_EVENT_IE_PLTYPE_UINT, "State" }, + [AST_EVENT_IE_CONTEXT] = { AST_EVENT_IE_PLTYPE_STR, "Context" }, + [AST_EVENT_IE_EID] = { AST_EVENT_IE_PLTYPE_RAW, "EntityID" }, + [AST_EVENT_IE_CEL_EVENT_TYPE] = { AST_EVENT_IE_PLTYPE_UINT, "CELEventType" }, + [AST_EVENT_IE_CEL_EVENT_TIME] = { AST_EVENT_IE_PLTYPE_UINT, "CELEventTime" }, + [AST_EVENT_IE_CEL_EVENT_TIME_USEC] = { AST_EVENT_IE_PLTYPE_UINT, "CELEventTimeUSec" }, + [AST_EVENT_IE_CEL_USEREVENT_NAME] = { AST_EVENT_IE_PLTYPE_UINT, "CELUserEventName" }, + [AST_EVENT_IE_CEL_CIDNAME] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDName" }, + [AST_EVENT_IE_CEL_CIDNUM] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDNum" }, + [AST_EVENT_IE_CEL_EXTEN] = { AST_EVENT_IE_PLTYPE_STR, "CELExten" }, + [AST_EVENT_IE_CEL_CONTEXT] = { AST_EVENT_IE_PLTYPE_STR, "CELContext" }, + [AST_EVENT_IE_CEL_CHANNAME] = { AST_EVENT_IE_PLTYPE_STR, "CELChanName" }, + [AST_EVENT_IE_CEL_APPNAME] = { AST_EVENT_IE_PLTYPE_STR, "CELAppName" }, + [AST_EVENT_IE_CEL_APPDATA] = { AST_EVENT_IE_PLTYPE_STR, "CELAppData" }, + [AST_EVENT_IE_CEL_AMAFLAGS] = { AST_EVENT_IE_PLTYPE_STR, "CELAMAFlags" }, + [AST_EVENT_IE_CEL_ACCTCODE] = { AST_EVENT_IE_PLTYPE_UINT, "CELAcctCode" }, + [AST_EVENT_IE_CEL_UNIQUEID] = { AST_EVENT_IE_PLTYPE_STR, "CELUniqueID" }, + [AST_EVENT_IE_CEL_USERFIELD] = { AST_EVENT_IE_PLTYPE_STR, "CELUserField" }, + [AST_EVENT_IE_CEL_CIDANI] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDani" }, + [AST_EVENT_IE_CEL_CIDRDNIS] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDrdnis" }, + [AST_EVENT_IE_CEL_CIDDNID] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDdnid" }, + [AST_EVENT_IE_CEL_PEER] = { AST_EVENT_IE_PLTYPE_STR, "CELPeer" }, + [AST_EVENT_IE_CEL_LINKEDID] = { AST_EVENT_IE_PLTYPE_STR, "CELLinkedID" }, + [AST_EVENT_IE_CEL_PEERACCT] = { AST_EVENT_IE_PLTYPE_STR, "CELPeerAcct" }, + [AST_EVENT_IE_CEL_EXTRA] = { AST_EVENT_IE_PLTYPE_STR, "CELExtra" }, + [AST_EVENT_IE_SECURITY_EVENT] = { AST_EVENT_IE_PLTYPE_STR, "SecurityEvent" }, + [AST_EVENT_IE_EVENT_VERSION] = { AST_EVENT_IE_PLTYPE_UINT, "EventVersion" }, + [AST_EVENT_IE_SERVICE] = { AST_EVENT_IE_PLTYPE_STR, "Service" }, + [AST_EVENT_IE_MODULE] = { AST_EVENT_IE_PLTYPE_STR, "Module" }, + [AST_EVENT_IE_ACCOUNT_ID] = { AST_EVENT_IE_PLTYPE_STR, "AccountID" }, + [AST_EVENT_IE_SESSION_ID] = { AST_EVENT_IE_PLTYPE_STR, "SessionID" }, + [AST_EVENT_IE_SESSION_TV] = { AST_EVENT_IE_PLTYPE_STR, "SessionTV" }, + [AST_EVENT_IE_ACL_NAME] = { AST_EVENT_IE_PLTYPE_STR, "ACLName" }, + [AST_EVENT_IE_LOCAL_ADDR] = { AST_EVENT_IE_PLTYPE_STR, "LocalAddress" }, + [AST_EVENT_IE_REMOTE_ADDR] = { AST_EVENT_IE_PLTYPE_STR, "RemoteAddress" }, + [AST_EVENT_IE_EVENT_TV] = { AST_EVENT_IE_PLTYPE_STR, "EventTV" }, + [AST_EVENT_IE_REQUEST_TYPE] = { AST_EVENT_IE_PLTYPE_STR, "RequestType" }, + [AST_EVENT_IE_REQUEST_PARAMS] = { AST_EVENT_IE_PLTYPE_STR, "RequestParams" }, + [AST_EVENT_IE_AUTH_METHOD] = { AST_EVENT_IE_PLTYPE_STR, "AuthMethod" }, + [AST_EVENT_IE_SEVERITY] = { AST_EVENT_IE_PLTYPE_STR, "Severity" }, + [AST_EVENT_IE_EXPECTED_ADDR] = { AST_EVENT_IE_PLTYPE_STR, "ExpectedAddress" }, + [AST_EVENT_IE_CHALLENGE] = { AST_EVENT_IE_PLTYPE_STR, "Challenge" }, + [AST_EVENT_IE_RESPONSE] = { AST_EVENT_IE_PLTYPE_STR, "Response" }, + [AST_EVENT_IE_EXPECTED_RESPONSE] = { AST_EVENT_IE_PLTYPE_STR, "ExpectedResponse" }, }; const char *ast_event_get_type_name(const struct ast_event *event) @@ -248,12 +262,12 @@ const char *ast_event_get_type_name(const struct ast_event *event) type = ast_event_get_type(event); - if (type >= AST_EVENT_TOTAL || type < 0) { + if (type < 0 || type >= ARRAY_LEN(event_names)) { ast_log(LOG_ERROR, "Invalid event type - '%d'\n", type); return ""; } - return event_names[type].name; + return event_names[type]; } int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type) @@ -261,10 +275,11 @@ int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type int i; for (i = 0; i < ARRAY_LEN(event_names); i++) { - if (strcasecmp(event_names[i].name, str)) + if (strcasecmp(event_names[i], str)) { continue; + } - *event_type = event_names[i].type; + *event_type = i; return 0; } @@ -273,31 +288,21 @@ int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type const char *ast_event_get_ie_type_name(enum ast_event_ie_type ie_type) { - if (ie_type <= 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= ARRAY_LEN(ie_maps)) { ast_log(LOG_ERROR, "Invalid IE type - '%d'\n", ie_type); return ""; } - if (ie_maps[ie_type].ie_type != ie_type) { - ast_log(LOG_ERROR, "The ie type passed in does not match the ie type defined in the ie table.\n"); - return ""; - } - return ie_maps[ie_type].name; } enum ast_event_ie_pltype ast_event_get_ie_pltype(enum ast_event_ie_type ie_type) { - if (ie_type <= 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= ARRAY_LEN(ie_maps)) { ast_log(LOG_ERROR, "Invalid IE type - '%d'\n", ie_type); return AST_EVENT_IE_PLTYPE_UNKNOWN; } - if (ie_maps[ie_type].ie_type != ie_type) { - ast_log(LOG_ERROR, "The ie type passed in does not match the ie type defined in the ie table.\n"); - return AST_EVENT_IE_PLTYPE_UNKNOWN; - } - return ie_maps[ie_type].ie_pltype; } @@ -306,10 +311,11 @@ int ast_event_str_to_ie_type(const char *str, enum ast_event_ie_type *ie_type) int i; for (i = 0; i < ARRAY_LEN(ie_maps); i++) { - if (strcasecmp(ie_maps[i].name, str)) + if (strcasecmp(ie_maps[i].name, str)) { continue; + } - *ie_type = ie_maps[i].ie_type; + *ie_type = i; return 0; } @@ -661,7 +667,7 @@ int ast_event_sub_append_ie_uint(struct ast_event_sub *sub, { struct ast_event_ie_val *ie_val; - if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) { return -1; } @@ -683,11 +689,13 @@ int ast_event_sub_append_ie_bitflags(struct ast_event_sub *sub, { struct ast_event_ie_val *ie_val; - if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) + if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) { return -1; + } - if (!(ie_val = ast_calloc(1, sizeof(*ie_val)))) + if (!(ie_val = ast_calloc(1, sizeof(*ie_val)))) { return -1; + } ie_val->ie_type = ie_type; ie_val->payload.uint = flags; @@ -703,7 +711,7 @@ int ast_event_sub_append_ie_exists(struct ast_event_sub *sub, { struct ast_event_ie_val *ie_val; - if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) { return -1; } @@ -724,7 +732,7 @@ int ast_event_sub_append_ie_str(struct ast_event_sub *sub, { struct ast_event_ie_val *ie_val; - if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) { return -1; } @@ -752,7 +760,7 @@ int ast_event_sub_append_ie_raw(struct ast_event_sub *sub, { struct ast_event_ie_val *ie_val; - if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) { return -1; } diff --git a/main/manager.c b/main/manager.c index 0670da36d8150e6ddd2d86cf259c76bdf366bb53..c7e4ce4c65e3fe90cff359fd750f70de54951ed5 100644 --- a/main/manager.c +++ b/main/manager.c @@ -74,6 +74,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/term.h" #include "asterisk/astobj2.h" #include "asterisk/features.h" +#include "asterisk/security_events.h" /*** DOCUMENTATION <manager name="Ping" language="en_US"> @@ -807,6 +808,7 @@ struct mansession_session { pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */ uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */ time_t sessionstart; /*!< Session start time */ + struct timeval sessionstart_tv; /*!< Session start time */ time_t sessiontimeout; /*!< Session timeout if HTTP */ char username[80]; /*!< Logged in username */ char challenge[10]; /*!< Authentication challenge */ @@ -834,6 +836,7 @@ struct mansession_session { */ struct mansession { struct mansession_session *session; + struct ast_tcptls_session_instance *tcptls_session; FILE *f; int fd; ast_mutex_t lock; @@ -1735,6 +1738,241 @@ static int set_eventmask(struct mansession *s, const char *eventmask) return maskint; } +static enum ast_security_event_transport_type mansession_get_transport(const struct mansession *s) +{ + return s->tcptls_session->parent->tls_cfg ? AST_SECURITY_EVENT_TRANSPORT_TLS : + AST_SECURITY_EVENT_TRANSPORT_TCP; +} + +static struct sockaddr_in *mansession_encode_sin_local(const struct mansession *s, + struct sockaddr_in *sin_local) +{ + *sin_local = s->tcptls_session->parent->local_address; + + return sin_local; +} + +static void report_invalid_user(const struct mansession *s, const char *username) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_inval_acct_id inval_acct_id = { + .common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID, + .common.version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION, + .common.service = "AMI", + .common.account_id = username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", s); + + ast_security_event_report(AST_SEC_EVT(&inval_acct_id)); +} + +static void report_failed_acl(const struct mansession *s, const char *username) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_failed_acl failed_acl_event = { + .common.event_type = AST_SECURITY_EVENT_FAILED_ACL, + .common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION, + .common.service = "AMI", + .common.account_id = username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + + ast_security_event_report(AST_SEC_EVT(&failed_acl_event)); +} + +static void report_inval_password(const struct mansession *s, const char *username) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_inval_password inval_password = { + .common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD, + .common.version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION, + .common.service = "AMI", + .common.account_id = username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + + ast_security_event_report(AST_SEC_EVT(&inval_password)); +} + +static void report_auth_success(const struct mansession *s) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_successful_auth successful_auth = { + .common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH, + .common.version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION, + .common.service = "AMI", + .common.account_id = s->session->username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + + ast_security_event_report(AST_SEC_EVT(&successful_auth)); +} + +static void report_req_not_allowed(const struct mansession *s, const char *action) +{ + struct sockaddr_in sin_local; + char session_id[32]; + char request_type[64]; + struct ast_security_event_req_not_allowed req_not_allowed = { + .common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED, + .common.version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION, + .common.service = "AMI", + .common.account_id = s->session->username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + + .request_type = request_type, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + snprintf(request_type, sizeof(request_type), "Action: %s", action); + + ast_security_event_report(AST_SEC_EVT(&req_not_allowed)); +} + +static void report_req_bad_format(const struct mansession *s, const char *action) +{ + struct sockaddr_in sin_local; + char session_id[32]; + char request_type[64]; + struct ast_security_event_req_bad_format req_bad_format = { + .common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT, + .common.version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION, + .common.service = "AMI", + .common.account_id = s->session->username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + + .request_type = request_type, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + snprintf(request_type, sizeof(request_type), "Action: %s", action); + + ast_security_event_report(AST_SEC_EVT(&req_bad_format)); +} + +static void report_failed_challenge_response(const struct mansession *s, + const char *response, const char *expected_response) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_chal_resp_failed chal_resp_failed = { + .common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED, + .common.version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION, + .common.service = "AMI", + .common.account_id = s->session->username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + + .challenge = s->session->challenge, + .response = response, + .expected_response = expected_response, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + + ast_security_event_report(AST_SEC_EVT(&chal_resp_failed)); +} + +static void report_session_limit(const struct mansession *s) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_session_limit session_limit = { + .common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT, + .common.version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION, + .common.service = "AMI", + .common.account_id = s->session->username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + + ast_security_event_report(AST_SEC_EVT(&session_limit)); +} + /* * Here we start with action_ handlers for AMI actions, * and the internal functions used by them. @@ -1757,8 +1995,10 @@ static int authenticate(struct mansession *s, const struct message *m) AST_RWLIST_WRLOCK(&users); if (!(user = get_manager_by_name_locked(username))) { + report_invalid_user(s, username); ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username); } else if (user->ha && !ast_apply_ha(user->ha, &(s->session->sin))) { + report_failed_acl(s, username); ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username); } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) { const char *key = astman_get_header(m, "Key"); @@ -1777,13 +2017,19 @@ static int authenticate(struct mansession *s, const struct message *m) len += sprintf(md5key + len, "%2.2x", digest[x]); if (!strcmp(md5key, key)) { error = 0; + } else { + report_failed_challenge_response(s, key, md5key); } } else { ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n", S_OR(s->session->challenge, "")); } - } else if (password && user->secret && !strcmp(password, user->secret)) { - error = 0; + } else if (user->secret) { + if (!strcmp(password, user->secret)) { + error = 0; + } else { + report_inval_password(s, username); + } } if (error) { @@ -1799,8 +2045,11 @@ static int authenticate(struct mansession *s, const struct message *m) s->session->writeperm = user->writeperm; s->session->writetimeout = user->writetimeout; s->session->sessionstart = time(NULL); + s->session->sessionstart_tv = ast_tvnow(); set_eventmask(s, astman_get_header(m, "Events")); + report_auth_success(s); + AST_RWLIST_UNLOCK(&users); return 0; } @@ -3550,6 +3799,7 @@ static int process_message(struct mansession *s, const struct message *m) ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action)); if (ast_strlen_zero(action)) { + report_req_bad_format(s, "NONE"); mansession_lock(s); astman_send_error(s, m, "Missing action in request"); mansession_unlock(s); @@ -3557,6 +3807,9 @@ static int process_message(struct mansession *s, const struct message *m) } if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) { + if (!s->session->authenticated) { + report_req_not_allowed(s, action); + } mansession_lock(s); astman_send_error(s, m, "Permission denied"); mansession_unlock(s); @@ -3566,6 +3819,7 @@ static int process_message(struct mansession *s, const struct message *m) if (!allowmultiplelogin && !s->session->authenticated && user && (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) { if (check_manager_session_inuse(user)) { + report_session_limit(s); sleep(1); mansession_lock(s); astman_send_error(s, m, "Login Already In Use"); @@ -3583,7 +3837,7 @@ static int process_message(struct mansession *s, const struct message *m) call_func = tmp->func; } else { astman_send_error(s, m, "Permission denied"); - tmp = NULL; + report_req_not_allowed(s, action); } break; } @@ -3595,6 +3849,9 @@ static int process_message(struct mansession *s, const struct message *m) ret = call_func(s, m); } else { char buf[512]; + if (!tmp) { + report_req_bad_format(s, action); + } snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action); mansession_lock(s); astman_send_error(s, m, buf); @@ -3733,7 +3990,9 @@ static void *session_do(void *data) { struct ast_tcptls_session_instance *ser = data; struct mansession_session *session = build_mansession(ser->remote_address); - struct mansession s = { NULL, }; + struct mansession s = { + .tcptls_session = data, + }; int flags; int res; diff --git a/main/security_events.c b/main/security_events.c new file mode 100644 index 0000000000000000000000000000000000000000..d1e2ac0ccd3e96a33e667ffe4d13264187f3c38d --- /dev/null +++ b/main/security_events.c @@ -0,0 +1,647 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009, Digium, Inc. + * + * Russell Bryant <russell@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * + * \brief Security Event Reporting Helpers + * + * \author Russell Bryant <russell@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/utils.h" +#include "asterisk/strings.h" +#include "asterisk/network.h" +#include "asterisk/security_events.h" + +static const size_t TIMESTAMP_STR_LEN = 32; + +static const struct { + const char *name; + uint32_t version; + enum ast_security_event_severity severity; +#define MAX_SECURITY_IES 12 + struct ast_security_event_ie_type required_ies[MAX_SECURITY_IES]; + struct ast_security_event_ie_type optional_ies[MAX_SECURITY_IES]; +#undef MAX_SECURITY_IES +} sec_events[AST_SECURITY_EVENT_NUM_TYPES] = { + +#define SEC_EVT_FIELD(e, field) (offsetof(struct ast_security_event_##e, field)) + +[AST_SECURITY_EVENT_FAILED_ACL] = { + .name = "FailedACL", + .version = AST_SECURITY_EVENT_FAILED_ACL_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_ACL_NAME, SEC_EVT_FIELD(failed_acl, acl_name) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_INVAL_ACCT_ID] = { + .name = "InvalidAccountID", + .version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_SESSION_LIMIT] = { + .name = "SessionLimit", + .version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_MEM_LIMIT] = { + .name = "MemoryLimit", + .version = AST_SECURITY_EVENT_MEM_LIMIT_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_LOAD_AVG] = { + .name = "LoadAverageLimit", + .version = AST_SECURITY_EVENT_LOAD_AVG_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_REQ_NO_SUPPORT] = { + .name = "RequestNotSupported", + .version = AST_SECURITY_EVENT_REQ_NO_SUPPORT_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_no_support, request_type) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_REQ_NOT_ALLOWED] = { + .name = "RequestNotAllowed", + .version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_not_allowed, request_type) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_REQUEST_PARAMS, SEC_EVT_FIELD(req_not_allowed, request_params) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED] = { + .name = "AuthMethodNotAllowed", + .version = AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_AUTH_METHOD, SEC_EVT_FIELD(auth_method_not_allowed, auth_method) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_REQ_BAD_FORMAT] = { + .name = "RequestBadFormat", + .version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_bad_format, request_type) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_REQUEST_PARAMS, SEC_EVT_FIELD(req_bad_format, request_params) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_SUCCESSFUL_AUTH] = { + .name = "SuccessfulAuth", + .version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_INFO, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_UNEXPECTED_ADDR] = { + .name = "UnexpectedAddress", + .version = AST_SECURITY_EVENT_UNEXPECTED_ADDR_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_EXPECTED_ADDR, SEC_EVT_FIELD(unexpected_addr, expected_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_CHAL_RESP_FAILED] = { + .name = "ChallengeResponseFailed", + .version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_CHALLENGE, SEC_EVT_FIELD(chal_resp_failed, challenge) }, + { AST_EVENT_IE_RESPONSE, SEC_EVT_FIELD(chal_resp_failed, response) }, + { AST_EVENT_IE_EXPECTED_RESPONSE, SEC_EVT_FIELD(chal_resp_failed, expected_response) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_INVAL_PASSWORD] = { + .name = "InvalidPassword", + .version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +#undef SEC_EVT_FIELD + +}; + +static const struct { + enum ast_security_event_severity severity; + const char *str; +} severities[] = { + { AST_SECURITY_EVENT_SEVERITY_INFO, "Informational" }, + { AST_SECURITY_EVENT_SEVERITY_ERROR, "Error" }, +}; + +const char *ast_security_event_severity_get_name( + const enum ast_security_event_severity severity) +{ + unsigned int i; + + for (i = 0; i < ARRAY_LEN(severities); i++) { + if (severities[i].severity == severity) { + return severities[i].str; + } + } + + return NULL; +} + +static int check_event_type(const enum ast_security_event_type event_type) +{ + if (event_type < 0 || event_type >= AST_SECURITY_EVENT_NUM_TYPES) { + ast_log(LOG_ERROR, "Invalid security event type %u\n", event_type); + return -1; + } + + return 0; +} + +const char *ast_security_event_get_name(const enum ast_security_event_type event_type) +{ + if (check_event_type(event_type)) { + return NULL; + } + + return sec_events[event_type].name; +} + +const struct ast_security_event_ie_type *ast_security_event_get_required_ies( + const enum ast_security_event_type event_type) +{ + if (check_event_type(event_type)) { + return NULL; + } + + return sec_events[event_type].required_ies; +} + +const struct ast_security_event_ie_type *ast_security_event_get_optional_ies( + const enum ast_security_event_type event_type) +{ + if (check_event_type(event_type)) { + return NULL; + } + + return sec_events[event_type].optional_ies; +} + +static void encode_timestamp(struct ast_str **str, const struct timeval *tv) +{ + ast_str_set(str, 0, "%u-%u", + (unsigned int) tv->tv_sec, + (unsigned int) tv->tv_usec); +} + +static struct ast_event *alloc_event(const struct ast_security_event_common *sec) +{ + struct ast_str *str = ast_str_alloca(TIMESTAMP_STR_LEN); + struct timeval tv = ast_tvnow(); + const char *severity_str; + + if (check_event_type(sec->event_type)) { + return NULL; + } + + encode_timestamp(&str, &tv); + + severity_str = S_OR( + ast_security_event_severity_get_name(sec_events[sec->event_type].severity), + "Unknown" + ); + + return ast_event_new(AST_EVENT_SECURITY, + AST_EVENT_IE_SECURITY_EVENT, AST_EVENT_IE_PLTYPE_UINT, sec->event_type, + AST_EVENT_IE_EVENT_VERSION, AST_EVENT_IE_PLTYPE_UINT, sec->version, + AST_EVENT_IE_EVENT_TV, AST_EVENT_IE_PLTYPE_STR, str->str, + AST_EVENT_IE_SERVICE, AST_EVENT_IE_PLTYPE_STR, sec->service, + AST_EVENT_IE_SEVERITY, AST_EVENT_IE_PLTYPE_STR, severity_str, + AST_EVENT_IE_END); +} + +static int add_timeval_ie(struct ast_event **event, enum ast_event_ie_type ie_type, + const struct timeval *tv) +{ + struct ast_str *str = ast_str_alloca(TIMESTAMP_STR_LEN); + + encode_timestamp(&str, tv); + + return ast_event_append_ie_str(event, ie_type, ast_str_buffer(str)); +} + +static int add_ipv4_ie(struct ast_event **event, enum ast_event_ie_type ie_type, + const struct ast_security_event_ipv4_addr *addr) +{ + struct ast_str *str = ast_str_alloca(64); + + ast_str_set(&str, 0, "IPV4/"); + + switch (addr->transport) { + case AST_SECURITY_EVENT_TRANSPORT_UDP: + ast_str_append(&str, 0, "UDP/"); + break; + case AST_SECURITY_EVENT_TRANSPORT_TCP: + ast_str_append(&str, 0, "TCP/"); + break; + case AST_SECURITY_EVENT_TRANSPORT_TLS: + ast_str_append(&str, 0, "TLS/"); + break; + } + + ast_str_append(&str, 0, "%s/%hu", + ast_inet_ntoa(addr->sin->sin_addr), + ntohs(addr->sin->sin_port)); + + return ast_event_append_ie_str(event, ie_type, ast_str_buffer(str)); +} + +enum ie_required { + NOT_REQUIRED, + REQUIRED +}; + +static int add_ie(struct ast_event **event, const struct ast_security_event_common *sec, + const struct ast_security_event_ie_type *ie_type, enum ie_required req) +{ + int res = 0; + + switch (ie_type->ie_type) { + case AST_EVENT_IE_SERVICE: + case AST_EVENT_IE_ACCOUNT_ID: + case AST_EVENT_IE_SESSION_ID: + case AST_EVENT_IE_MODULE: + case AST_EVENT_IE_ACL_NAME: + case AST_EVENT_IE_REQUEST_TYPE: + case AST_EVENT_IE_REQUEST_PARAMS: + case AST_EVENT_IE_AUTH_METHOD: + case AST_EVENT_IE_CHALLENGE: + case AST_EVENT_IE_RESPONSE: + case AST_EVENT_IE_EXPECTED_RESPONSE: + { + const char *str; + + str = *((const char **)(((const char *) sec) + ie_type->offset)); + + if (req && !str) { + ast_log(LOG_WARNING, "Required IE '%d' for security event " + "type '%d' not present\n", ie_type->ie_type, + sec->event_type); + res = -1; + } + + if (str) { + res = ast_event_append_ie_str(event, ie_type->ie_type, str); + } + + break; + } + case AST_EVENT_IE_EVENT_VERSION: + { + uint32_t val; + val = *((const uint32_t *)(((const char *) sec) + ie_type->offset)); + res = ast_event_append_ie_uint(event, ie_type->ie_type, val); + break; + } + case AST_EVENT_IE_LOCAL_ADDR: + case AST_EVENT_IE_REMOTE_ADDR: + case AST_EVENT_IE_EXPECTED_ADDR: + { + const struct ast_security_event_ipv4_addr *addr; + + addr = (const struct ast_security_event_ipv4_addr *)(((const char *) sec) + ie_type->offset); + + if (req && !addr->sin) { + ast_log(LOG_WARNING, "Required IE '%d' for security event " + "type '%d' not present\n", ie_type->ie_type, + sec->event_type); + res = -1; + } + + if (addr->sin) { + res = add_ipv4_ie(event, ie_type->ie_type, addr); + } + break; + } + case AST_EVENT_IE_SESSION_TV: + { + const struct timeval *tval; + + tval = *((const struct timeval **)(((const char *) sec) + ie_type->offset)); + + if (req && !tval) { + ast_log(LOG_WARNING, "Required IE '%d' for security event " + "type '%d' not present\n", ie_type->ie_type, + sec->event_type); + res = -1; + } + + if (tval) { + add_timeval_ie(event, ie_type->ie_type, tval); + } + + break; + } + case AST_EVENT_IE_EVENT_TV: + case AST_EVENT_IE_SEVERITY: + /* Added automatically, nothing to do here. */ + break; + default: + ast_log(LOG_WARNING, "Unhandled IE type '%d', this security event " + "will be missing data.\n", ie_type->ie_type); + break; + } + + return res; +} + +static int handle_security_event(const struct ast_security_event_common *sec) +{ + struct ast_event *event; + const struct ast_security_event_ie_type *ies; + unsigned int i; + + if (!(event = alloc_event(sec))) { + return -1; + } + + for (ies = ast_security_event_get_required_ies(sec->event_type), i = 0; + ies[i].ie_type != AST_EVENT_IE_END; + i++) { + if (add_ie(&event, sec, ies + i, REQUIRED)) { + goto return_error; + } + } + + for (ies = ast_security_event_get_optional_ies(sec->event_type), i = 0; + ies[i].ie_type != AST_EVENT_IE_END; + i++) { + if (add_ie(&event, sec, ies + i, NOT_REQUIRED)) { + goto return_error; + } + } + + + if (ast_event_queue(event)) { + goto return_error; + } + + return 0; + +return_error: + if (event) { + ast_event_destroy(event); + } + + return -1; +} + +int ast_security_event_report(const struct ast_security_event_common *sec) +{ + int res; + + if (sec->event_type < 0 || sec->event_type >= AST_SECURITY_EVENT_NUM_TYPES) { + ast_log(LOG_ERROR, "Invalid security event type\n"); + return -1; + } + + if (!sec_events[sec->event_type].name) { + ast_log(LOG_WARNING, "Security event type %u not handled\n", + sec->event_type); + return -1; + } + + if (sec->version != sec_events[sec->event_type].version) { + ast_log(LOG_WARNING, "Security event %u version mismatch\n", + sec->event_type); + return -1; + } + + res = handle_security_event(sec); + + return res; +} + + diff --git a/res/res_security_log.c b/res/res_security_log.c new file mode 100644 index 0000000000000000000000000000000000000000..2e3d4af52ac98ee74fd418c7f07bf334ac9a7004 --- /dev/null +++ b/res/res_security_log.c @@ -0,0 +1,163 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009, Digium, Inc. + * + * Russell Bryant <russell@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * + * \author Russell Bryant <russell@digium.com> + * + * \brief Security Event Logging + * + * \todo Make informational security events optional + * \todo Escape quotes in string payload IE contents + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); + +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/event.h" +#include "asterisk/threadstorage.h" +#include "asterisk/strings.h" +#include "asterisk/security_events.h" + +static const char LOG_SECURITY_NAME[] = "SECURITY"; + +static int LOG_SECURITY; + +static struct ast_event_sub *security_event_sub; + +AST_THREADSTORAGE(security_event_buf); +static const size_t SECURITY_EVENT_BUF_INIT_LEN = 256; + +enum ie_required { + NOT_REQUIRED, + REQUIRED +}; + +static int ie_is_present(const struct ast_event *event, + const enum ast_event_ie_type ie_type) +{ + return (ast_event_get_ie_raw(event, ie_type) != NULL); +} + +static void append_ie(struct ast_str **str, const struct ast_event *event, + const enum ast_event_ie_type ie_type, enum ie_required required) +{ + if (!required && !ie_is_present(event, ie_type)) { + /* Optional IE isn't present. Ignore. */ + return; + } + + /* At this point, it _better_ be there! */ + ast_assert(ie_is_present(event, ie_type)); + + switch (ast_event_get_ie_pltype(ie_type)) { + case AST_EVENT_IE_PLTYPE_UINT: + ast_str_append(str, 0, ",%s=\"%u\"", + ast_event_get_ie_type_name(ie_type), + ast_event_get_ie_uint(event, ie_type)); + break; + case AST_EVENT_IE_PLTYPE_STR: + ast_str_append(str, 0, ",%s=\"%s\"", + ast_event_get_ie_type_name(ie_type), + ast_event_get_ie_str(event, ie_type)); + break; + case AST_EVENT_IE_PLTYPE_BITFLAGS: + ast_str_append(str, 0, ",%s=\"%u\"", + ast_event_get_ie_type_name(ie_type), + ast_event_get_ie_bitflags(event, ie_type)); + break; + case AST_EVENT_IE_PLTYPE_UNKNOWN: + case AST_EVENT_IE_PLTYPE_EXISTS: + case AST_EVENT_IE_PLTYPE_RAW: + ast_log(LOG_WARNING, "Unexpected payload type for IE '%s'\n", + ast_event_get_ie_type_name(ie_type)); + break; + } +} + +static void append_ies(struct ast_str **str, const struct ast_event *event, + const struct ast_security_event_ie_type *ies, enum ie_required required) +{ + unsigned int i; + + for (i = 0; ies[i].ie_type != AST_EVENT_IE_END; i++) { + append_ie(str, event, ies[i].ie_type, required); + } +} + +static void security_event_cb(const struct ast_event *event, void *data) +{ + struct ast_str *str; + enum ast_security_event_type event_type; + + if (!(str = ast_str_thread_get(&security_event_buf, + SECURITY_EVENT_BUF_INIT_LEN))) { + return; + } + + /* Note that the event type is guaranteed to be valid here. */ + event_type = ast_event_get_ie_uint(event, AST_EVENT_IE_SECURITY_EVENT); + ast_assert(event_type >= 0 && event_type < AST_SECURITY_EVENT_NUM_TYPES); + + ast_str_set(&str, 0, "%s=\"%s\"", + ast_event_get_ie_type_name(AST_EVENT_IE_SECURITY_EVENT), + ast_security_event_get_name(event_type)); + + append_ies(&str, event, + ast_security_event_get_required_ies(event_type), REQUIRED); + append_ies(&str, event, + ast_security_event_get_optional_ies(event_type), NOT_REQUIRED); + + ast_log_dynamic_level(LOG_SECURITY, "%s\n", ast_str_buffer(str)); +} + +static int load_module(void) +{ + if ((LOG_SECURITY = ast_logger_register_level(LOG_SECURITY_NAME)) == -1) { + return AST_MODULE_LOAD_DECLINE; + } + + if (!(security_event_sub = ast_event_subscribe(AST_EVENT_SECURITY, + security_event_cb, "Security Event Logger", + NULL, AST_EVENT_IE_END))) { + ast_logger_unregister_level(LOG_SECURITY_NAME); + LOG_SECURITY = -1; + return AST_MODULE_LOAD_DECLINE; + } + + ast_verb(3, "Security Logging Enabled\n"); + + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + if (security_event_sub) { + security_event_sub = ast_event_unsubscribe(security_event_sub); + } + + ast_verb(3, "Security Logging Disabled\n"); + + return 0; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Security Event Logging"); diff --git a/tests/test_ami_security_events.sh b/tests/test_ami_security_events.sh new file mode 100755 index 0000000000000000000000000000000000000000..6f125dc5a45409e435fc22c5aafebde3b159bd2f --- /dev/null +++ b/tests/test_ami_security_events.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# manager.conf: +# +# [general] +# ... +# allowmultipleconnects=no +# ... +# +# [russell] +# secret=blah123 +# read = system,call,log,verbose,command,agent,user,config +# write = system,call,log,verbose,command,agent,user,config +# deny=0.0.0.0/0.0.0.0 +# permit=127.0.0.1/255.255.255.255 +# +# [russell2] +# secret=blah123 +# read = system,call,log,verbose,command,agent,user,config +# write = system,call,log,verbose,command,agent,user,config +# deny=127.0.0.1/255.255.255.255 + +# Invalid User +printf "Action: Login\r\nUsername: foo\r\nSecret: moo\r\n\r\n" | nc localhost 5038 + +# Invalid Secret +printf "Action: Login\r\nUsername: russell\r\nSecret: moo\r\n\r\n" | nc localhost 5038 + +# Auth Success +printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\n" | nc -w 1 localhost 5038 + +# Failed ACL +printf "Action: Login\r\nUsername: russell2\r\nSecret: blah123\r\n\r\n" | nc -w 1 localhost 5038 + +# Request Not Allowed +printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\nAction: Originate\r\n\r\n" | nc -w 1 localhost 5038 + +# Request Bad Format +printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\nAction: FakeActionBLAH\r\n\r\n" | nc -w 1 localhost 5038 + +# Failed Challenge Response +printf "Action: Challenge\r\nUsername: russell\r\nAuthType: MD5\r\n\r\nAction: Login\r\nUsername: russell\r\nAuthType: MD5\r\nKey: 00000000\r\n\r\n" | nc localhost 5038 + +# Session Limit +printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\n" | nc -w 5 localhost 5038 & +printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\n" | nc -w 1 localhost 5038 + diff --git a/tests/test_security_events.c b/tests/test_security_events.c new file mode 100644 index 0000000000000000000000000000000000000000..33b40e87715631784708ddd681677cc30c1772af --- /dev/null +++ b/tests/test_security_events.c @@ -0,0 +1,625 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009, Digium, Inc. + * + * Russell Bryant <russell@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Test security event generation + * + * \author Russell Bryant <russell@digium.com> + */ + +/*** MODULEINFO + <defaultenabled>no</defaultenabled> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/module.h" +#include "asterisk/cli.h" +#include "asterisk/utils.h" +#include "asterisk/security_events.h" + +static void evt_gen_failed_acl(void); +static void evt_gen_inval_acct_id(void); +static void evt_gen_session_limit(void); +static void evt_gen_mem_limit(void); +static void evt_gen_load_avg(void); +static void evt_gen_req_no_support(void); +static void evt_gen_req_not_allowed(void); +static void evt_gen_auth_method_not_allowed(void); +static void evt_gen_req_bad_format(void); +static void evt_gen_successful_auth(void); +static void evt_gen_unexpected_addr(void); +static void evt_gen_chal_resp_failed(void); +static void evt_gen_inval_password(void); + +typedef void (*evt_generator)(void); +static const evt_generator evt_generators[AST_SECURITY_EVENT_NUM_TYPES] = { + [AST_SECURITY_EVENT_FAILED_ACL] = evt_gen_failed_acl, + [AST_SECURITY_EVENT_INVAL_ACCT_ID] = evt_gen_inval_acct_id, + [AST_SECURITY_EVENT_SESSION_LIMIT] = evt_gen_session_limit, + [AST_SECURITY_EVENT_MEM_LIMIT] = evt_gen_mem_limit, + [AST_SECURITY_EVENT_LOAD_AVG] = evt_gen_load_avg, + [AST_SECURITY_EVENT_REQ_NO_SUPPORT] = evt_gen_req_no_support, + [AST_SECURITY_EVENT_REQ_NOT_ALLOWED] = evt_gen_req_not_allowed, + [AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED] = evt_gen_auth_method_not_allowed, + [AST_SECURITY_EVENT_REQ_BAD_FORMAT] = evt_gen_req_bad_format, + [AST_SECURITY_EVENT_SUCCESSFUL_AUTH] = evt_gen_successful_auth, + [AST_SECURITY_EVENT_UNEXPECTED_ADDR] = evt_gen_unexpected_addr, + [AST_SECURITY_EVENT_CHAL_RESP_FAILED] = evt_gen_chal_resp_failed, + [AST_SECURITY_EVENT_INVAL_PASSWORD] = evt_gen_inval_password, +}; + +static void evt_gen_failed_acl(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_failed_acl failed_acl_event = { + .common.event_type = AST_SECURITY_EVENT_FAILED_ACL, + .common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "Username", + .common.session_id = "Session123", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + + .acl_name = "TEST_ACL", + }; + + inet_aton("192.168.1.1", &sin_local.sin_addr); + sin_local.sin_port = htons(12121); + + inet_aton("192.168.1.2", &sin_remote.sin_addr); + sin_remote.sin_port = htons(12345); + + ast_security_event_report(AST_SEC_EVT(&failed_acl_event)); +} + +static void evt_gen_inval_acct_id(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_inval_acct_id inval_acct_id = { + .common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID, + .common.version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "FakeUser", + .common.session_id = "Session456", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + }; + + inet_aton("10.1.2.3", &sin_local.sin_addr); + sin_local.sin_port = htons(4321); + + inet_aton("10.1.2.4", &sin_remote.sin_addr); + sin_remote.sin_port = htons(1234); + + ast_security_event_report(AST_SEC_EVT(&inval_acct_id)); +} + +static void evt_gen_session_limit(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_session_limit session_limit = { + .common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT, + .common.version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "Jenny", + .common.session_id = "8675309", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_TLS, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_TLS, + }, + }; + + inet_aton("10.5.4.3", &sin_local.sin_addr); + sin_local.sin_port = htons(4444); + + inet_aton("10.5.4.2", &sin_remote.sin_addr); + sin_remote.sin_port = htons(3333); + + ast_security_event_report(AST_SEC_EVT(&session_limit)); +} + +static void evt_gen_mem_limit(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_mem_limit mem_limit = { + .common.event_type = AST_SECURITY_EVENT_MEM_LIMIT, + .common.version = AST_SECURITY_EVENT_MEM_LIMIT_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "Felix", + .common.session_id = "Session2604", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + }; + + inet_aton("10.10.10.10", &sin_local.sin_addr); + sin_local.sin_port = htons(555); + + inet_aton("10.10.10.12", &sin_remote.sin_addr); + sin_remote.sin_port = htons(5656); + + ast_security_event_report(AST_SEC_EVT(&mem_limit)); +} + +static void evt_gen_load_avg(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_load_avg load_avg = { + .common.event_type = AST_SECURITY_EVENT_LOAD_AVG, + .common.version = AST_SECURITY_EVENT_LOAD_AVG_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "GuestAccount", + .common.session_id = "XYZ123", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + }; + + inet_aton("10.11.12.13", &sin_local.sin_addr); + sin_local.sin_port = htons(9876); + + inet_aton("10.12.11.10", &sin_remote.sin_addr); + sin_remote.sin_port = htons(9825); + + ast_security_event_report(AST_SEC_EVT(&load_avg)); +} + +static void evt_gen_req_no_support(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_req_no_support req_no_support = { + .common.event_type = AST_SECURITY_EVENT_REQ_NO_SUPPORT, + .common.version = AST_SECURITY_EVENT_REQ_NO_SUPPORT_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "George", + .common.session_id = "asdkl23478289lasdkf", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + + .request_type = "MakeMeDinner", + }; + + inet_aton("10.110.120.130", &sin_local.sin_addr); + sin_local.sin_port = htons(9888); + + inet_aton("10.120.110.100", &sin_remote.sin_addr); + sin_remote.sin_port = htons(9777); + + ast_security_event_report(AST_SEC_EVT(&req_no_support)); +} + +static void evt_gen_req_not_allowed(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_req_not_allowed req_not_allowed = { + .common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED, + .common.version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "George", + .common.session_id = "alksdjf023423h4lka0df", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + + .request_type = "MakeMeBreakfast", + .request_params = "BACONNNN!", + }; + + inet_aton("10.110.120.130", &sin_local.sin_addr); + sin_local.sin_port = htons(9888); + + inet_aton("10.120.110.100", &sin_remote.sin_addr); + sin_remote.sin_port = htons(9777); + + ast_security_event_report(AST_SEC_EVT(&req_not_allowed)); +} + +static void evt_gen_auth_method_not_allowed(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_auth_method_not_allowed auth_method_not_allowed = { + .common.event_type = AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED, + .common.version = AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "Bob", + .common.session_id = "010101010101", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + + .auth_method = "PlainText" + }; + + inet_aton("10.110.120.135", &sin_local.sin_addr); + sin_local.sin_port = htons(8754); + + inet_aton("10.120.110.105", &sin_remote.sin_addr); + sin_remote.sin_port = htons(8745); + + ast_security_event_report(AST_SEC_EVT(&auth_method_not_allowed)); +} + +static void evt_gen_req_bad_format(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_req_bad_format req_bad_format = { + .common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT, + .common.version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "Larry", + .common.session_id = "838383fhfhf83hf8h3f8h", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + + .request_type = "CheeseBurger", + .request_params = "Onions,Swiss,MotorOil", + }; + + inet_aton("10.110.220.230", &sin_local.sin_addr); + sin_local.sin_port = htons(1212); + + inet_aton("10.120.210.200", &sin_remote.sin_addr); + sin_remote.sin_port = htons(2121); + + ast_security_event_report(AST_SEC_EVT(&req_bad_format)); +} + +static void evt_gen_successful_auth(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_successful_auth successful_auth = { + .common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH, + .common.version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "ValidUser", + .common.session_id = "Session456", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + }; + + inet_aton("10.1.2.3", &sin_local.sin_addr); + sin_local.sin_port = htons(4321); + + inet_aton("10.1.2.4", &sin_remote.sin_addr); + sin_remote.sin_port = htons(1234); + + ast_security_event_report(AST_SEC_EVT(&successful_auth)); +} + +static void evt_gen_unexpected_addr(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_expected = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_unexpected_addr unexpected_addr = { + .common.event_type = AST_SECURITY_EVENT_UNEXPECTED_ADDR, + .common.version = AST_SECURITY_EVENT_UNEXPECTED_ADDR_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "CoolUser", + .common.session_id = "Session789", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + + .expected_addr = { + .sin = &sin_expected, + .transport = AST_SECURITY_EVENT_TRANSPORT_UDP, + }, + }; + + inet_aton("10.1.2.3", &sin_local.sin_addr); + sin_local.sin_port = htons(4321); + + inet_aton("10.1.2.4", &sin_remote.sin_addr); + sin_remote.sin_port = htons(1234); + + inet_aton("10.1.2.5", &sin_expected.sin_addr); + sin_expected.sin_port = htons(2343); + + ast_security_event_report(AST_SEC_EVT(&unexpected_addr)); +} + +static void evt_gen_chal_resp_failed(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_chal_resp_failed chal_resp_failed = { + .common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED, + .common.version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "SuperDuperUser", + .common.session_id = "Session1231231231", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + + .challenge = "8adf8a9sd8fas9df23ljk4", + .response = "9u3jlaksdjflakjsdfoi23", + .expected_response = "oiafaljhadf9834luahk3k", + }; + + inet_aton("10.1.2.3", &sin_local.sin_addr); + sin_local.sin_port = htons(4321); + + inet_aton("10.1.2.4", &sin_remote.sin_addr); + sin_remote.sin_port = htons(1234); + + ast_security_event_report(AST_SEC_EVT(&chal_resp_failed)); +} + +static void evt_gen_inval_password(void) +{ + struct sockaddr_in sin_local = { + .sin_family = AF_INET + }; + struct sockaddr_in sin_remote = { + .sin_family = AF_INET + }; + struct timeval session_tv = ast_tvnow(); + struct ast_security_event_inval_password inval_password = { + .common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD, + .common.version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION, + .common.service = "TEST", + .common.module = AST_MODULE, + .common.account_id = "AccountIDGoesHere", + .common.session_id = "SessionIDGoesHere", + .common.session_tv = &session_tv, + .common.local_addr = { + .sin = &sin_local, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + .common.remote_addr = { + .sin = &sin_remote, + .transport = AST_SECURITY_EVENT_TRANSPORT_TCP, + }, + }; + + inet_aton("10.200.100.30", &sin_local.sin_addr); + sin_local.sin_port = htons(4321); + + inet_aton("10.200.100.40", &sin_remote.sin_addr); + sin_remote.sin_port = htons(1234); + + ast_security_event_report(AST_SEC_EVT(&inval_password)); +} + +static void gen_events(struct ast_cli_args *a) +{ + unsigned int i; + + ast_cli(a->fd, "Generating some security events ...\n"); + + for (i = 0; i < ARRAY_LEN(evt_generators); i++) { + const char *event_type = ast_security_event_get_name(i); + + if (!evt_generators[i]) { + ast_cli(a->fd, "*** No event generator for event type '%s' ***\n", + event_type); + continue; + } + + ast_cli(a->fd, "Generating a '%s' security event ...\n", event_type); + + evt_generators[i](); + } + + ast_cli(a->fd, "Security event generation complete.\n"); +} + +static char *handle_cli_sec_evt_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "securityevents test generation"; + e->usage = "" + "Usage: securityevents test generation" + ""; + return NULL; + case CLI_GENERATE: + return NULL; + case CLI_HANDLER: + gen_events(a); + return CLI_SUCCESS; + } + + return CLI_FAILURE; +} + +static struct ast_cli_entry cli_sec_evt[] = { + AST_CLI_DEFINE(handle_cli_sec_evt_test, "Test security event generation"), +}; + +static int unload_module(void) +{ + return ast_cli_unregister_multiple(cli_sec_evt, ARRAY_LEN(cli_sec_evt)); +} + +static int load_module(void) +{ + int res; + + res = ast_cli_register_multiple(cli_sec_evt, ARRAY_LEN(cli_sec_evt)); + + return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Test Security Event Generation");