diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/event.c | 156 | ||||
-rw-r--r-- | main/manager.c | 267 | ||||
-rw-r--r-- | main/security_events.c | 647 |
3 files changed, 992 insertions, 78 deletions
diff --git a/main/event.c b/main/event.c index 6a70a6741..44c65714d 100644 --- a/main/event.c +++ b/main/event.c @@ -183,63 +183,77 @@ static struct { }; /*! - * The index of each entry _must_ match the event type number! + * \brief Event Names */ -static struct event_name { - enum ast_event_type type; - const char *name; -} event_names[] = { - { 0, "" }, - { AST_EVENT_CUSTOM, "Custom" }, - { AST_EVENT_MWI, "MWI" }, - { AST_EVENT_SUB, "Subscription" }, - { AST_EVENT_UNSUB, "Unsubscription" }, - { AST_EVENT_DEVICE_STATE, "DeviceState" }, - { AST_EVENT_DEVICE_STATE_CHANGE, "DeviceStateChange" }, - { AST_EVENT_CEL, "CEL" }, +static const char * const event_names[AST_EVENT_TOTAL] = { + [AST_EVENT_CUSTOM] = "Custom", + [AST_EVENT_MWI] = "MWI", + [AST_EVENT_SUB] = "Subscription", + [AST_EVENT_UNSUB] = "Unsubscription", + [AST_EVENT_DEVICE_STATE] = "DeviceState", + [AST_EVENT_DEVICE_STATE_CHANGE] = "DeviceStateChange", + [AST_EVENT_CEL] = "CEL", + [AST_EVENT_SECURITY] = "Security", }; /*! - * The index of each entry _must_ match the event ie number! + * \brief IE payload types and names */ -static struct ie_map { - enum ast_event_ie_type ie_type; +static const struct ie_map { enum ast_event_ie_pltype ie_pltype; const char *name; -} ie_maps[] = { - { 0, 0, "" }, - { AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, "NewMessages" }, - { AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, "OldMessages" }, - { AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, "Mailbox" }, - { AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, "UniqueID" }, - { AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, "EventType" }, - { AST_EVENT_IE_EXISTS, AST_EVENT_IE_PLTYPE_UINT, "Exists" }, - { AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "Device" }, - { AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, "State" }, - { AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "Context" }, - { AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, "EntityID" }, - { AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, "CELEventType" }, - { AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, "CELEventTime" }, - { AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, "CELEventTimeUSec" }, - { AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_UINT, "CELUserEventName" }, - { AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, "CELCIDName" }, - { AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, "CELCIDNum" }, - { AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, "CELExten" }, - { AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "CELContext" }, - { AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, "CELChanName" }, - { AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, "CELAppName" }, - { AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, "CELAppData" }, - { AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_STR, "CELAMAFlags" }, - { AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_UINT, "CELAcctCode" }, - { AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, "CELUniqueID" }, - { AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, "CELUserField" }, - { AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, "CELCIDani" }, - { AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, "CELCIDrdnis" }, - { AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, "CELCIDdnid" }, - { AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, "CELPeer" }, - { AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, "CELLinkedID" }, - { AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, "CELPeerAcct" }, - { AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, "CELExtra" }, +} ie_maps[AST_EVENT_IE_TOTAL] = { + [AST_EVENT_IE_NEWMSGS] = { AST_EVENT_IE_PLTYPE_UINT, "NewMessages" }, + [AST_EVENT_IE_OLDMSGS] = { AST_EVENT_IE_PLTYPE_UINT, "OldMessages" }, + [AST_EVENT_IE_MAILBOX] = { AST_EVENT_IE_PLTYPE_STR, "Mailbox" }, + [AST_EVENT_IE_UNIQUEID] = { AST_EVENT_IE_PLTYPE_UINT, "UniqueID" }, + [AST_EVENT_IE_EVENTTYPE] = { AST_EVENT_IE_PLTYPE_UINT, "EventType" }, + [AST_EVENT_IE_EXISTS] = { AST_EVENT_IE_PLTYPE_UINT, "Exists" }, + [AST_EVENT_IE_DEVICE] = { AST_EVENT_IE_PLTYPE_STR, "Device" }, + [AST_EVENT_IE_STATE] = { AST_EVENT_IE_PLTYPE_UINT, "State" }, + [AST_EVENT_IE_CONTEXT] = { AST_EVENT_IE_PLTYPE_STR, "Context" }, + [AST_EVENT_IE_EID] = { AST_EVENT_IE_PLTYPE_RAW, "EntityID" }, + [AST_EVENT_IE_CEL_EVENT_TYPE] = { AST_EVENT_IE_PLTYPE_UINT, "CELEventType" }, + [AST_EVENT_IE_CEL_EVENT_TIME] = { AST_EVENT_IE_PLTYPE_UINT, "CELEventTime" }, + [AST_EVENT_IE_CEL_EVENT_TIME_USEC] = { AST_EVENT_IE_PLTYPE_UINT, "CELEventTimeUSec" }, + [AST_EVENT_IE_CEL_USEREVENT_NAME] = { AST_EVENT_IE_PLTYPE_UINT, "CELUserEventName" }, + [AST_EVENT_IE_CEL_CIDNAME] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDName" }, + [AST_EVENT_IE_CEL_CIDNUM] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDNum" }, + [AST_EVENT_IE_CEL_EXTEN] = { AST_EVENT_IE_PLTYPE_STR, "CELExten" }, + [AST_EVENT_IE_CEL_CONTEXT] = { AST_EVENT_IE_PLTYPE_STR, "CELContext" }, + [AST_EVENT_IE_CEL_CHANNAME] = { AST_EVENT_IE_PLTYPE_STR, "CELChanName" }, + [AST_EVENT_IE_CEL_APPNAME] = { AST_EVENT_IE_PLTYPE_STR, "CELAppName" }, + [AST_EVENT_IE_CEL_APPDATA] = { AST_EVENT_IE_PLTYPE_STR, "CELAppData" }, + [AST_EVENT_IE_CEL_AMAFLAGS] = { AST_EVENT_IE_PLTYPE_STR, "CELAMAFlags" }, + [AST_EVENT_IE_CEL_ACCTCODE] = { AST_EVENT_IE_PLTYPE_UINT, "CELAcctCode" }, + [AST_EVENT_IE_CEL_UNIQUEID] = { AST_EVENT_IE_PLTYPE_STR, "CELUniqueID" }, + [AST_EVENT_IE_CEL_USERFIELD] = { AST_EVENT_IE_PLTYPE_STR, "CELUserField" }, + [AST_EVENT_IE_CEL_CIDANI] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDani" }, + [AST_EVENT_IE_CEL_CIDRDNIS] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDrdnis" }, + [AST_EVENT_IE_CEL_CIDDNID] = { AST_EVENT_IE_PLTYPE_STR, "CELCIDdnid" }, + [AST_EVENT_IE_CEL_PEER] = { AST_EVENT_IE_PLTYPE_STR, "CELPeer" }, + [AST_EVENT_IE_CEL_LINKEDID] = { AST_EVENT_IE_PLTYPE_STR, "CELLinkedID" }, + [AST_EVENT_IE_CEL_PEERACCT] = { AST_EVENT_IE_PLTYPE_STR, "CELPeerAcct" }, + [AST_EVENT_IE_CEL_EXTRA] = { AST_EVENT_IE_PLTYPE_STR, "CELExtra" }, + [AST_EVENT_IE_SECURITY_EVENT] = { AST_EVENT_IE_PLTYPE_STR, "SecurityEvent" }, + [AST_EVENT_IE_EVENT_VERSION] = { AST_EVENT_IE_PLTYPE_UINT, "EventVersion" }, + [AST_EVENT_IE_SERVICE] = { AST_EVENT_IE_PLTYPE_STR, "Service" }, + [AST_EVENT_IE_MODULE] = { AST_EVENT_IE_PLTYPE_STR, "Module" }, + [AST_EVENT_IE_ACCOUNT_ID] = { AST_EVENT_IE_PLTYPE_STR, "AccountID" }, + [AST_EVENT_IE_SESSION_ID] = { AST_EVENT_IE_PLTYPE_STR, "SessionID" }, + [AST_EVENT_IE_SESSION_TV] = { AST_EVENT_IE_PLTYPE_STR, "SessionTV" }, + [AST_EVENT_IE_ACL_NAME] = { AST_EVENT_IE_PLTYPE_STR, "ACLName" }, + [AST_EVENT_IE_LOCAL_ADDR] = { AST_EVENT_IE_PLTYPE_STR, "LocalAddress" }, + [AST_EVENT_IE_REMOTE_ADDR] = { AST_EVENT_IE_PLTYPE_STR, "RemoteAddress" }, + [AST_EVENT_IE_EVENT_TV] = { AST_EVENT_IE_PLTYPE_STR, "EventTV" }, + [AST_EVENT_IE_REQUEST_TYPE] = { AST_EVENT_IE_PLTYPE_STR, "RequestType" }, + [AST_EVENT_IE_REQUEST_PARAMS] = { AST_EVENT_IE_PLTYPE_STR, "RequestParams" }, + [AST_EVENT_IE_AUTH_METHOD] = { AST_EVENT_IE_PLTYPE_STR, "AuthMethod" }, + [AST_EVENT_IE_SEVERITY] = { AST_EVENT_IE_PLTYPE_STR, "Severity" }, + [AST_EVENT_IE_EXPECTED_ADDR] = { AST_EVENT_IE_PLTYPE_STR, "ExpectedAddress" }, + [AST_EVENT_IE_CHALLENGE] = { AST_EVENT_IE_PLTYPE_STR, "Challenge" }, + [AST_EVENT_IE_RESPONSE] = { AST_EVENT_IE_PLTYPE_STR, "Response" }, + [AST_EVENT_IE_EXPECTED_RESPONSE] = { AST_EVENT_IE_PLTYPE_STR, "ExpectedResponse" }, }; const char *ast_event_get_type_name(const struct ast_event *event) @@ -248,12 +262,12 @@ const char *ast_event_get_type_name(const struct ast_event *event) type = ast_event_get_type(event); - if (type >= AST_EVENT_TOTAL || type < 0) { + if (type < 0 || type >= ARRAY_LEN(event_names)) { ast_log(LOG_ERROR, "Invalid event type - '%d'\n", type); return ""; } - return event_names[type].name; + return event_names[type]; } int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type) @@ -261,10 +275,11 @@ int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type int i; for (i = 0; i < ARRAY_LEN(event_names); i++) { - if (strcasecmp(event_names[i].name, str)) + if (strcasecmp(event_names[i], str)) { continue; + } - *event_type = event_names[i].type; + *event_type = i; return 0; } @@ -273,31 +288,21 @@ int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type const char *ast_event_get_ie_type_name(enum ast_event_ie_type ie_type) { - if (ie_type <= 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= ARRAY_LEN(ie_maps)) { ast_log(LOG_ERROR, "Invalid IE type - '%d'\n", ie_type); return ""; } - if (ie_maps[ie_type].ie_type != ie_type) { - ast_log(LOG_ERROR, "The ie type passed in does not match the ie type defined in the ie table.\n"); - return ""; - } - return ie_maps[ie_type].name; } enum ast_event_ie_pltype ast_event_get_ie_pltype(enum ast_event_ie_type ie_type) { - if (ie_type <= 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= ARRAY_LEN(ie_maps)) { ast_log(LOG_ERROR, "Invalid IE type - '%d'\n", ie_type); return AST_EVENT_IE_PLTYPE_UNKNOWN; } - if (ie_maps[ie_type].ie_type != ie_type) { - ast_log(LOG_ERROR, "The ie type passed in does not match the ie type defined in the ie table.\n"); - return AST_EVENT_IE_PLTYPE_UNKNOWN; - } - return ie_maps[ie_type].ie_pltype; } @@ -306,10 +311,11 @@ int ast_event_str_to_ie_type(const char *str, enum ast_event_ie_type *ie_type) int i; for (i = 0; i < ARRAY_LEN(ie_maps); i++) { - if (strcasecmp(ie_maps[i].name, str)) + if (strcasecmp(ie_maps[i].name, str)) { continue; + } - *ie_type = ie_maps[i].ie_type; + *ie_type = i; return 0; } @@ -661,7 +667,7 @@ int ast_event_sub_append_ie_uint(struct ast_event_sub *sub, { struct ast_event_ie_val *ie_val; - if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) { return -1; } @@ -683,11 +689,13 @@ int ast_event_sub_append_ie_bitflags(struct ast_event_sub *sub, { struct ast_event_ie_val *ie_val; - if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) + if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) { return -1; + } - if (!(ie_val = ast_calloc(1, sizeof(*ie_val)))) + if (!(ie_val = ast_calloc(1, sizeof(*ie_val)))) { return -1; + } ie_val->ie_type = ie_type; ie_val->payload.uint = flags; @@ -703,7 +711,7 @@ int ast_event_sub_append_ie_exists(struct ast_event_sub *sub, { struct ast_event_ie_val *ie_val; - if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) { return -1; } @@ -724,7 +732,7 @@ int ast_event_sub_append_ie_str(struct ast_event_sub *sub, { struct ast_event_ie_val *ie_val; - if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) { return -1; } @@ -752,7 +760,7 @@ int ast_event_sub_append_ie_raw(struct ast_event_sub *sub, { struct ast_event_ie_val *ie_val; - if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) { + if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) { return -1; } diff --git a/main/manager.c b/main/manager.c index 0670da36d..c7e4ce4c6 100644 --- a/main/manager.c +++ b/main/manager.c @@ -74,6 +74,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/term.h" #include "asterisk/astobj2.h" #include "asterisk/features.h" +#include "asterisk/security_events.h" /*** DOCUMENTATION <manager name="Ping" language="en_US"> @@ -807,6 +808,7 @@ struct mansession_session { pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */ uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */ time_t sessionstart; /*!< Session start time */ + struct timeval sessionstart_tv; /*!< Session start time */ time_t sessiontimeout; /*!< Session timeout if HTTP */ char username[80]; /*!< Logged in username */ char challenge[10]; /*!< Authentication challenge */ @@ -834,6 +836,7 @@ struct mansession_session { */ struct mansession { struct mansession_session *session; + struct ast_tcptls_session_instance *tcptls_session; FILE *f; int fd; ast_mutex_t lock; @@ -1735,6 +1738,241 @@ static int set_eventmask(struct mansession *s, const char *eventmask) return maskint; } +static enum ast_security_event_transport_type mansession_get_transport(const struct mansession *s) +{ + return s->tcptls_session->parent->tls_cfg ? AST_SECURITY_EVENT_TRANSPORT_TLS : + AST_SECURITY_EVENT_TRANSPORT_TCP; +} + +static struct sockaddr_in *mansession_encode_sin_local(const struct mansession *s, + struct sockaddr_in *sin_local) +{ + *sin_local = s->tcptls_session->parent->local_address; + + return sin_local; +} + +static void report_invalid_user(const struct mansession *s, const char *username) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_inval_acct_id inval_acct_id = { + .common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID, + .common.version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION, + .common.service = "AMI", + .common.account_id = username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", s); + + ast_security_event_report(AST_SEC_EVT(&inval_acct_id)); +} + +static void report_failed_acl(const struct mansession *s, const char *username) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_failed_acl failed_acl_event = { + .common.event_type = AST_SECURITY_EVENT_FAILED_ACL, + .common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION, + .common.service = "AMI", + .common.account_id = username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + + ast_security_event_report(AST_SEC_EVT(&failed_acl_event)); +} + +static void report_inval_password(const struct mansession *s, const char *username) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_inval_password inval_password = { + .common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD, + .common.version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION, + .common.service = "AMI", + .common.account_id = username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + + ast_security_event_report(AST_SEC_EVT(&inval_password)); +} + +static void report_auth_success(const struct mansession *s) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_successful_auth successful_auth = { + .common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH, + .common.version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION, + .common.service = "AMI", + .common.account_id = s->session->username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + + ast_security_event_report(AST_SEC_EVT(&successful_auth)); +} + +static void report_req_not_allowed(const struct mansession *s, const char *action) +{ + struct sockaddr_in sin_local; + char session_id[32]; + char request_type[64]; + struct ast_security_event_req_not_allowed req_not_allowed = { + .common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED, + .common.version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION, + .common.service = "AMI", + .common.account_id = s->session->username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + + .request_type = request_type, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + snprintf(request_type, sizeof(request_type), "Action: %s", action); + + ast_security_event_report(AST_SEC_EVT(&req_not_allowed)); +} + +static void report_req_bad_format(const struct mansession *s, const char *action) +{ + struct sockaddr_in sin_local; + char session_id[32]; + char request_type[64]; + struct ast_security_event_req_bad_format req_bad_format = { + .common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT, + .common.version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION, + .common.service = "AMI", + .common.account_id = s->session->username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + + .request_type = request_type, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + snprintf(request_type, sizeof(request_type), "Action: %s", action); + + ast_security_event_report(AST_SEC_EVT(&req_bad_format)); +} + +static void report_failed_challenge_response(const struct mansession *s, + const char *response, const char *expected_response) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_chal_resp_failed chal_resp_failed = { + .common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED, + .common.version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION, + .common.service = "AMI", + .common.account_id = s->session->username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + + .challenge = s->session->challenge, + .response = response, + .expected_response = expected_response, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + + ast_security_event_report(AST_SEC_EVT(&chal_resp_failed)); +} + +static void report_session_limit(const struct mansession *s) +{ + struct sockaddr_in sin_local; + char session_id[32]; + struct ast_security_event_session_limit session_limit = { + .common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT, + .common.version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION, + .common.service = "AMI", + .common.account_id = s->session->username, + .common.session_tv = &s->session->sessionstart_tv, + .common.local_addr = { + .sin = mansession_encode_sin_local(s, &sin_local), + .transport = mansession_get_transport(s), + }, + .common.remote_addr = { + .sin = &s->session->sin, + .transport = mansession_get_transport(s), + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", s->session); + + ast_security_event_report(AST_SEC_EVT(&session_limit)); +} + /* * Here we start with action_ handlers for AMI actions, * and the internal functions used by them. @@ -1757,8 +1995,10 @@ static int authenticate(struct mansession *s, const struct message *m) AST_RWLIST_WRLOCK(&users); if (!(user = get_manager_by_name_locked(username))) { + report_invalid_user(s, username); ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username); } else if (user->ha && !ast_apply_ha(user->ha, &(s->session->sin))) { + report_failed_acl(s, username); ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username); } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) { const char *key = astman_get_header(m, "Key"); @@ -1777,13 +2017,19 @@ static int authenticate(struct mansession *s, const struct message *m) len += sprintf(md5key + len, "%2.2x", digest[x]); if (!strcmp(md5key, key)) { error = 0; + } else { + report_failed_challenge_response(s, key, md5key); } } else { ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n", S_OR(s->session->challenge, "")); } - } else if (password && user->secret && !strcmp(password, user->secret)) { - error = 0; + } else if (user->secret) { + if (!strcmp(password, user->secret)) { + error = 0; + } else { + report_inval_password(s, username); + } } if (error) { @@ -1799,8 +2045,11 @@ static int authenticate(struct mansession *s, const struct message *m) s->session->writeperm = user->writeperm; s->session->writetimeout = user->writetimeout; s->session->sessionstart = time(NULL); + s->session->sessionstart_tv = ast_tvnow(); set_eventmask(s, astman_get_header(m, "Events")); + report_auth_success(s); + AST_RWLIST_UNLOCK(&users); return 0; } @@ -3550,6 +3799,7 @@ static int process_message(struct mansession *s, const struct message *m) ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action)); if (ast_strlen_zero(action)) { + report_req_bad_format(s, "NONE"); mansession_lock(s); astman_send_error(s, m, "Missing action in request"); mansession_unlock(s); @@ -3557,6 +3807,9 @@ static int process_message(struct mansession *s, const struct message *m) } if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) { + if (!s->session->authenticated) { + report_req_not_allowed(s, action); + } mansession_lock(s); astman_send_error(s, m, "Permission denied"); mansession_unlock(s); @@ -3566,6 +3819,7 @@ static int process_message(struct mansession *s, const struct message *m) if (!allowmultiplelogin && !s->session->authenticated && user && (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) { if (check_manager_session_inuse(user)) { + report_session_limit(s); sleep(1); mansession_lock(s); astman_send_error(s, m, "Login Already In Use"); @@ -3583,7 +3837,7 @@ static int process_message(struct mansession *s, const struct message *m) call_func = tmp->func; } else { astman_send_error(s, m, "Permission denied"); - tmp = NULL; + report_req_not_allowed(s, action); } break; } @@ -3595,6 +3849,9 @@ static int process_message(struct mansession *s, const struct message *m) ret = call_func(s, m); } else { char buf[512]; + if (!tmp) { + report_req_bad_format(s, action); + } snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action); mansession_lock(s); astman_send_error(s, m, buf); @@ -3733,7 +3990,9 @@ static void *session_do(void *data) { struct ast_tcptls_session_instance *ser = data; struct mansession_session *session = build_mansession(ser->remote_address); - struct mansession s = { NULL, }; + struct mansession s = { + .tcptls_session = data, + }; int flags; int res; diff --git a/main/security_events.c b/main/security_events.c new file mode 100644 index 000000000..d1e2ac0cc --- /dev/null +++ b/main/security_events.c @@ -0,0 +1,647 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009, Digium, Inc. + * + * Russell Bryant <russell@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * + * \brief Security Event Reporting Helpers + * + * \author Russell Bryant <russell@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/utils.h" +#include "asterisk/strings.h" +#include "asterisk/network.h" +#include "asterisk/security_events.h" + +static const size_t TIMESTAMP_STR_LEN = 32; + +static const struct { + const char *name; + uint32_t version; + enum ast_security_event_severity severity; +#define MAX_SECURITY_IES 12 + struct ast_security_event_ie_type required_ies[MAX_SECURITY_IES]; + struct ast_security_event_ie_type optional_ies[MAX_SECURITY_IES]; +#undef MAX_SECURITY_IES +} sec_events[AST_SECURITY_EVENT_NUM_TYPES] = { + +#define SEC_EVT_FIELD(e, field) (offsetof(struct ast_security_event_##e, field)) + +[AST_SECURITY_EVENT_FAILED_ACL] = { + .name = "FailedACL", + .version = AST_SECURITY_EVENT_FAILED_ACL_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_ACL_NAME, SEC_EVT_FIELD(failed_acl, acl_name) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_INVAL_ACCT_ID] = { + .name = "InvalidAccountID", + .version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_SESSION_LIMIT] = { + .name = "SessionLimit", + .version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_MEM_LIMIT] = { + .name = "MemoryLimit", + .version = AST_SECURITY_EVENT_MEM_LIMIT_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_LOAD_AVG] = { + .name = "LoadAverageLimit", + .version = AST_SECURITY_EVENT_LOAD_AVG_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_REQ_NO_SUPPORT] = { + .name = "RequestNotSupported", + .version = AST_SECURITY_EVENT_REQ_NO_SUPPORT_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_no_support, request_type) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_REQ_NOT_ALLOWED] = { + .name = "RequestNotAllowed", + .version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_not_allowed, request_type) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_REQUEST_PARAMS, SEC_EVT_FIELD(req_not_allowed, request_params) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED] = { + .name = "AuthMethodNotAllowed", + .version = AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_AUTH_METHOD, SEC_EVT_FIELD(auth_method_not_allowed, auth_method) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_REQ_BAD_FORMAT] = { + .name = "RequestBadFormat", + .version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_bad_format, request_type) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_REQUEST_PARAMS, SEC_EVT_FIELD(req_bad_format, request_params) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_SUCCESSFUL_AUTH] = { + .name = "SuccessfulAuth", + .version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_INFO, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_UNEXPECTED_ADDR] = { + .name = "UnexpectedAddress", + .version = AST_SECURITY_EVENT_UNEXPECTED_ADDR_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_EXPECTED_ADDR, SEC_EVT_FIELD(unexpected_addr, expected_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_CHAL_RESP_FAILED] = { + .name = "ChallengeResponseFailed", + .version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_CHALLENGE, SEC_EVT_FIELD(chal_resp_failed, challenge) }, + { AST_EVENT_IE_RESPONSE, SEC_EVT_FIELD(chal_resp_failed, response) }, + { AST_EVENT_IE_EXPECTED_RESPONSE, SEC_EVT_FIELD(chal_resp_failed, expected_response) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +[AST_SECURITY_EVENT_INVAL_PASSWORD] = { + .name = "InvalidPassword", + .version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION, + .severity = AST_SECURITY_EVENT_SEVERITY_ERROR, + .required_ies = { + { AST_EVENT_IE_EVENT_TV, 0 }, + { AST_EVENT_IE_SEVERITY, 0 }, + { AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) }, + { AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) }, + { AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) }, + { AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) }, + { AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) }, + { AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) }, + { AST_EVENT_IE_END, 0 } + }, + .optional_ies = { + { AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) }, + { AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) }, + { AST_EVENT_IE_END, 0 } + }, +}, + +#undef SEC_EVT_FIELD + +}; + +static const struct { + enum ast_security_event_severity severity; + const char *str; +} severities[] = { + { AST_SECURITY_EVENT_SEVERITY_INFO, "Informational" }, + { AST_SECURITY_EVENT_SEVERITY_ERROR, "Error" }, +}; + +const char *ast_security_event_severity_get_name( + const enum ast_security_event_severity severity) +{ + unsigned int i; + + for (i = 0; i < ARRAY_LEN(severities); i++) { + if (severities[i].severity == severity) { + return severities[i].str; + } + } + + return NULL; +} + +static int check_event_type(const enum ast_security_event_type event_type) +{ + if (event_type < 0 || event_type >= AST_SECURITY_EVENT_NUM_TYPES) { + ast_log(LOG_ERROR, "Invalid security event type %u\n", event_type); + return -1; + } + + return 0; +} + +const char *ast_security_event_get_name(const enum ast_security_event_type event_type) +{ + if (check_event_type(event_type)) { + return NULL; + } + + return sec_events[event_type].name; +} + +const struct ast_security_event_ie_type *ast_security_event_get_required_ies( + const enum ast_security_event_type event_type) +{ + if (check_event_type(event_type)) { + return NULL; + } + + return sec_events[event_type].required_ies; +} + +const struct ast_security_event_ie_type *ast_security_event_get_optional_ies( + const enum ast_security_event_type event_type) +{ + if (check_event_type(event_type)) { + return NULL; + } + + return sec_events[event_type].optional_ies; +} + +static void encode_timestamp(struct ast_str **str, const struct timeval *tv) +{ + ast_str_set(str, 0, "%u-%u", + (unsigned int) tv->tv_sec, + (unsigned int) tv->tv_usec); +} + +static struct ast_event *alloc_event(const struct ast_security_event_common *sec) +{ + struct ast_str *str = ast_str_alloca(TIMESTAMP_STR_LEN); + struct timeval tv = ast_tvnow(); + const char *severity_str; + + if (check_event_type(sec->event_type)) { + return NULL; + } + + encode_timestamp(&str, &tv); + + severity_str = S_OR( + ast_security_event_severity_get_name(sec_events[sec->event_type].severity), + "Unknown" + ); + + return ast_event_new(AST_EVENT_SECURITY, + AST_EVENT_IE_SECURITY_EVENT, AST_EVENT_IE_PLTYPE_UINT, sec->event_type, + AST_EVENT_IE_EVENT_VERSION, AST_EVENT_IE_PLTYPE_UINT, sec->version, + AST_EVENT_IE_EVENT_TV, AST_EVENT_IE_PLTYPE_STR, str->str, + AST_EVENT_IE_SERVICE, AST_EVENT_IE_PLTYPE_STR, sec->service, + AST_EVENT_IE_SEVERITY, AST_EVENT_IE_PLTYPE_STR, severity_str, + AST_EVENT_IE_END); +} + +static int add_timeval_ie(struct ast_event **event, enum ast_event_ie_type ie_type, + const struct timeval *tv) +{ + struct ast_str *str = ast_str_alloca(TIMESTAMP_STR_LEN); + + encode_timestamp(&str, tv); + + return ast_event_append_ie_str(event, ie_type, ast_str_buffer(str)); +} + +static int add_ipv4_ie(struct ast_event **event, enum ast_event_ie_type ie_type, + const struct ast_security_event_ipv4_addr *addr) +{ + struct ast_str *str = ast_str_alloca(64); + + ast_str_set(&str, 0, "IPV4/"); + + switch (addr->transport) { + case AST_SECURITY_EVENT_TRANSPORT_UDP: + ast_str_append(&str, 0, "UDP/"); + break; + case AST_SECURITY_EVENT_TRANSPORT_TCP: + ast_str_append(&str, 0, "TCP/"); + break; + case AST_SECURITY_EVENT_TRANSPORT_TLS: + ast_str_append(&str, 0, "TLS/"); + break; + } + + ast_str_append(&str, 0, "%s/%hu", + ast_inet_ntoa(addr->sin->sin_addr), + ntohs(addr->sin->sin_port)); + + return ast_event_append_ie_str(event, ie_type, ast_str_buffer(str)); +} + +enum ie_required { + NOT_REQUIRED, + REQUIRED +}; + +static int add_ie(struct ast_event **event, const struct ast_security_event_common *sec, + const struct ast_security_event_ie_type *ie_type, enum ie_required req) +{ + int res = 0; + + switch (ie_type->ie_type) { + case AST_EVENT_IE_SERVICE: + case AST_EVENT_IE_ACCOUNT_ID: + case AST_EVENT_IE_SESSION_ID: + case AST_EVENT_IE_MODULE: + case AST_EVENT_IE_ACL_NAME: + case AST_EVENT_IE_REQUEST_TYPE: + case AST_EVENT_IE_REQUEST_PARAMS: + case AST_EVENT_IE_AUTH_METHOD: + case AST_EVENT_IE_CHALLENGE: + case AST_EVENT_IE_RESPONSE: + case AST_EVENT_IE_EXPECTED_RESPONSE: + { + const char *str; + + str = *((const char **)(((const char *) sec) + ie_type->offset)); + + if (req && !str) { + ast_log(LOG_WARNING, "Required IE '%d' for security event " + "type '%d' not present\n", ie_type->ie_type, + sec->event_type); + res = -1; + } + + if (str) { + res = ast_event_append_ie_str(event, ie_type->ie_type, str); + } + + break; + } + case AST_EVENT_IE_EVENT_VERSION: + { + uint32_t val; + val = *((const uint32_t *)(((const char *) sec) + ie_type->offset)); + res = ast_event_append_ie_uint(event, ie_type->ie_type, val); + break; + } + case AST_EVENT_IE_LOCAL_ADDR: + case AST_EVENT_IE_REMOTE_ADDR: + case AST_EVENT_IE_EXPECTED_ADDR: + { + const struct ast_security_event_ipv4_addr *addr; + + addr = (const struct ast_security_event_ipv4_addr *)(((const char *) sec) + ie_type->offset); + + if (req && !addr->sin) { + ast_log(LOG_WARNING, "Required IE '%d' for security event " + "type '%d' not present\n", ie_type->ie_type, + sec->event_type); + res = -1; + } + + if (addr->sin) { + res = add_ipv4_ie(event, ie_type->ie_type, addr); + } + break; + } + case AST_EVENT_IE_SESSION_TV: + { + const struct timeval *tval; + + tval = *((const struct timeval **)(((const char *) sec) + ie_type->offset)); + + if (req && !tval) { + ast_log(LOG_WARNING, "Required IE '%d' for security event " + "type '%d' not present\n", ie_type->ie_type, + sec->event_type); + res = -1; + } + + if (tval) { + add_timeval_ie(event, ie_type->ie_type, tval); + } + + break; + } + case AST_EVENT_IE_EVENT_TV: + case AST_EVENT_IE_SEVERITY: + /* Added automatically, nothing to do here. */ + break; + default: + ast_log(LOG_WARNING, "Unhandled IE type '%d', this security event " + "will be missing data.\n", ie_type->ie_type); + break; + } + + return res; +} + +static int handle_security_event(const struct ast_security_event_common *sec) +{ + struct ast_event *event; + const struct ast_security_event_ie_type *ies; + unsigned int i; + + if (!(event = alloc_event(sec))) { + return -1; + } + + for (ies = ast_security_event_get_required_ies(sec->event_type), i = 0; + ies[i].ie_type != AST_EVENT_IE_END; + i++) { + if (add_ie(&event, sec, ies + i, REQUIRED)) { + goto return_error; + } + } + + for (ies = ast_security_event_get_optional_ies(sec->event_type), i = 0; + ies[i].ie_type != AST_EVENT_IE_END; + i++) { + if (add_ie(&event, sec, ies + i, NOT_REQUIRED)) { + goto return_error; + } + } + + + if (ast_event_queue(event)) { + goto return_error; + } + + return 0; + +return_error: + if (event) { + ast_event_destroy(event); + } + + return -1; +} + +int ast_security_event_report(const struct ast_security_event_common *sec) +{ + int res; + + if (sec->event_type < 0 || sec->event_type >= AST_SECURITY_EVENT_NUM_TYPES) { + ast_log(LOG_ERROR, "Invalid security event type\n"); + return -1; + } + + if (!sec_events[sec->event_type].name) { + ast_log(LOG_WARNING, "Security event type %u not handled\n", + sec->event_type); + return -1; + } + + if (sec->version != sec_events[sec->event_type].version) { + ast_log(LOG_WARNING, "Security event %u version mismatch\n", + sec->event_type); + return -1; + } + + res = handle_security_event(sec); + + return res; +} + + |