diff options
Diffstat (limited to 'channels/sip')
-rw-r--r-- | channels/sip/include/security_events.h | 43 | ||||
-rw-r--r-- | channels/sip/include/sip.h | 46 | ||||
-rw-r--r-- | channels/sip/security_events.c | 398 |
3 files changed, 485 insertions, 2 deletions
diff --git a/channels/sip/include/security_events.h b/channels/sip/include/security_events.h new file mode 100644 index 000000000..cee2fa707 --- /dev/null +++ b/channels/sip/include/security_events.h @@ -0,0 +1,43 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2011, Digium, Inc. + * + * Michael L. Young <elgueromexicano@gmail.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 Generate security events in the SIP channel + * + * \author Michael L. Young <elgueromexicano@gmail.com> + */ + +#include "sip.h" + +#ifndef _SIP_SECURITY_EVENTS_H +#define _SIP_SECURITY_EVENTS_H + +void sip_report_invalid_peer(const struct sip_pvt *p); +void sip_report_failed_acl(const struct sip_pvt *p, const char *aclname); +void sip_report_inval_password(const struct sip_pvt *p, const char *responsechallenge, const char *responsehash); +void sip_report_auth_success(const struct sip_pvt *p, uint32_t *using_password); +void sip_report_session_limit(const struct sip_pvt *p); +void sip_report_failed_challenge_response(const struct sip_pvt *p, const char *response, const char *expected_response); +void sip_report_chal_sent(const struct sip_pvt *p); +void sip_report_inval_transport(const struct sip_pvt *p, const char *transport); +void sip_digest_parser(char *c, struct digestkeys *keys); +int sip_report_security_event(const struct sip_pvt *p, const struct sip_request *req, const int res); + +#endif diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index 7e0f2d261..baec6eee2 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -33,6 +33,7 @@ #include "asterisk/app.h" #include "asterisk/astobj.h" #include "asterisk/indications.h" +#include "asterisk/security_events.h" #ifndef FALSE #define FALSE 0 @@ -42,7 +43,7 @@ #define TRUE 1 #endif -/* Arguments for find_peer */ +/* Arguments for sip_find_peer */ #define FINDUSERS (1 << 0) #define FINDPEERS (1 << 1) #define FINDALLDEVICES (FINDUSERS | FINDPEERS) @@ -359,6 +360,8 @@ #define SIP_PAGE3_FLAGS_TO_COPY \ (SIP_PAGE3_SNOM_AOC | SIP_PAGE3_SRTP_TAG_32) +#define CHECK_AUTH_BUF_INITLEN 256 + /*@}*/ /*----------------------------------------------------------*/ @@ -381,6 +384,19 @@ enum sip_result { AST_FAILURE = -1, /*!< Failure code */ }; +/*! \brief The results from handling an invite request + * + * \note Start at these values so we do not conflict with + * check_auth_results values when returning from + * handle_request_invite. check_auth_results only returned during + * authentication routines + * */ +enum inv_req_result { + INV_REQ_SUCCESS = 11, /*!< Success code */ + INV_REQ_FAILED = 10, /*!< Failure code */ + INV_REQ_ERROR = 9, /*!< Error code */ +}; + /*! \brief States for the INVITE transaction, not the dialog * \note this is for the INVITE that sets up the dialog */ @@ -472,7 +488,8 @@ enum check_auth_result { AUTH_PEER_NOT_DYNAMIC = -6, AUTH_ACL_FAILED = -7, AUTH_BAD_TRANSPORT = -8, - AUTH_RTP_FAILED = 9, + AUTH_RTP_FAILED = -9, + AUTH_SESSION_LIMIT = -10, }; /*! \brief States for outbound registrations (with register= lines in sip.conf */ @@ -633,6 +650,13 @@ enum sip_tcptls_alert { TCPTLS_ALERT_STOP, /*!< \brief A request to stop the tcp_handler thread */ }; +enum digest_keys { + K_RESP, + K_URI, + K_USER, + K_NONCE, + K_LAST +}; /*----------------------------------------------------------*/ /*---- STRUCTS ----*/ @@ -1797,4 +1821,22 @@ static const struct cfsip_options { { SIP_OPT_TARGET_DIALOG,NOT_SUPPORTED, "tdialog" }, }; +struct digestkeys { + const char *key; + const char *s; +}; + +AST_THREADSTORAGE(check_auth_buf); + +/*----------------------------------------------------------*/ +/*---- FUNCTIONS ----*/ +/*----------------------------------------------------------*/ + +struct sip_peer *sip_find_peer(const char *peer, struct ast_sockaddr *addr, int realtime, int which_objects, int devstate_only, int transport); +void sip_auth_headers(enum sip_auth_type code, char **header, char **respheader); +const char *sip_get_header(const struct sip_request *req, const char *name); +const char *sip_get_transport(enum sip_transport t); +void *sip_unref_peer(struct sip_peer *peer, char *tag); +struct sip_peer *sip_ref_peer(struct sip_peer *peer, char *tag); + #endif diff --git a/channels/sip/security_events.c b/channels/sip/security_events.c new file mode 100644 index 000000000..8af4f29f9 --- /dev/null +++ b/channels/sip/security_events.c @@ -0,0 +1,398 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2011, Digium, Inc. + * + * Michael L. Young <elgueromexicano@gmail.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 Generate security events in the SIP channel + * + * \author Michael L. Young <elgueromexicano@gmail.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "include/sip.h" +#include "include/security_events.h" + +/*! \brief Determine transport type used to receive request*/ + +static enum ast_security_event_transport_type security_event_get_transport(const struct sip_pvt *p) +{ + int res = 0; + + switch (p->socket.type) { + case SIP_TRANSPORT_UDP: + return AST_SECURITY_EVENT_TRANSPORT_UDP; + case SIP_TRANSPORT_TCP: + return AST_SECURITY_EVENT_TRANSPORT_TCP; + case SIP_TRANSPORT_TLS: + return AST_SECURITY_EVENT_TRANSPORT_TLS; + } + + return res; +} + +static struct sockaddr_in *security_event_encode_sin_local(const struct sip_pvt *p, struct sockaddr_in *sin_local) +{ + ast_sockaddr_to_sin(&p->ourip, sin_local); + + return sin_local; +} + +static struct sockaddr_in *security_event_encode_sin_remote(const struct sip_pvt *p, struct sockaddr_in *sin_remote) +{ + ast_sockaddr_to_sin(&p->sa, sin_remote); + + return sin_remote; +} + +void sip_report_invalid_peer(const struct sip_pvt *p) +{ + char session_id[32]; + struct sockaddr_in sin_local; + struct sockaddr_in sin_remote; + + 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 = "SIP", + .common.account_id = p->exten, + .common.local_addr = { + .sin = security_event_encode_sin_local(p, &sin_local), + .transport = security_event_get_transport(p) + }, + .common.remote_addr = { + .sin = security_event_encode_sin_remote(p, &sin_remote), + .transport = security_event_get_transport(p) + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", p); + + ast_security_event_report(AST_SEC_EVT(&inval_acct_id)); +} + +void sip_report_failed_acl(const struct sip_pvt *p, const char *aclname) +{ + char session_id[32]; + struct sockaddr_in sin_local; + struct sockaddr_in sin_remote; + + 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 = "SIP", + .common.account_id = p->exten, + .common.local_addr = { + .sin = security_event_encode_sin_local(p, &sin_local), + .transport = security_event_get_transport(p) + }, + .common.remote_addr = { + .sin = security_event_encode_sin_remote(p, &sin_remote), + .transport = security_event_get_transport(p) + }, + .common.session_id = session_id, + .acl_name = aclname, + }; + + snprintf(session_id, sizeof(session_id), "%p", p); + + ast_security_event_report(AST_SEC_EVT(&failed_acl_event)); +} + +void sip_report_inval_password(const struct sip_pvt *p, const char *response_challenge, const char *response_hash) +{ + char session_id[32]; + struct sockaddr_in sin_local; + struct sockaddr_in sin_remote; + + 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 = "SIP", + .common.account_id = p->exten, + .common.local_addr = { + .sin = security_event_encode_sin_local(p, &sin_local), + .transport = security_event_get_transport(p) + }, + .common.remote_addr = { + .sin = security_event_encode_sin_remote(p, &sin_remote), + .transport = security_event_get_transport(p) + }, + .common.session_id = session_id, + + .challenge = p->randdata, + .received_challenge = response_challenge, + .received_hash = response_hash, + }; + + snprintf(session_id, sizeof(session_id), "%p", p); + + ast_security_event_report(AST_SEC_EVT(&inval_password)); +} + +void sip_report_auth_success(const struct sip_pvt *p, uint32_t *using_password) +{ + char session_id[32]; + struct sockaddr_in sin_local; + struct sockaddr_in sin_remote; + + 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 = "SIP", + .common.account_id = p->exten, + .common.local_addr = { + .sin = security_event_encode_sin_local(p, &sin_local), + .transport = security_event_get_transport(p) + }, + .common.remote_addr = { + .sin = security_event_encode_sin_remote(p, &sin_remote), + .transport = security_event_get_transport(p) + }, + .common.session_id = session_id, + .using_password = using_password, + }; + + snprintf(session_id, sizeof(session_id), "%p", p); + + ast_security_event_report(AST_SEC_EVT(&successful_auth)); +} + +void sip_report_session_limit(const struct sip_pvt *p) +{ + char session_id[32]; + struct sockaddr_in sin_local; + struct sockaddr_in sin_remote; + + 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 = "SIP", + .common.account_id = p->exten, + .common.local_addr = { + .sin = security_event_encode_sin_local(p, &sin_local), + .transport = security_event_get_transport(p) + }, + .common.remote_addr = { + .sin = security_event_encode_sin_remote(p, &sin_remote), + .transport = security_event_get_transport(p) + }, + .common.session_id = session_id, + }; + + snprintf(session_id, sizeof(session_id), "%p", p); + + ast_security_event_report(AST_SEC_EVT(&session_limit)); +} + +void sip_report_failed_challenge_response(const struct sip_pvt *p, const char *response, const char *expected_response) +{ + char session_id[32]; + struct sockaddr_in sin_local; + struct sockaddr_in sin_remote; + char account_id[256]; + + 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 = "SIP", + .common.account_id = account_id, + .common.local_addr = { + .sin = security_event_encode_sin_local(p, &sin_local), + .transport = security_event_get_transport(p) + }, + .common.remote_addr = { + .sin = security_event_encode_sin_remote(p, &sin_remote), + .transport = security_event_get_transport(p) + }, + .common.session_id = session_id, + + .challenge = p->randdata, + .response = response, + .expected_response = expected_response, + }; + + if (!ast_strlen_zero(p->from)) { /* When dialing, show account making call */ + ast_copy_string(account_id, p->from, sizeof(account_id)); + } else { + ast_copy_string(account_id, p->exten, sizeof(account_id)); + } + + snprintf(session_id, sizeof(session_id), "%p", p); + + ast_security_event_report(AST_SEC_EVT(&chal_resp_failed)); +} + +void sip_report_chal_sent(const struct sip_pvt *p) +{ + char session_id[32]; + struct sockaddr_in sin_local; + struct sockaddr_in sin_remote; + char account_id[256]; + + struct ast_security_event_chal_sent chal_sent = { + .common.event_type = AST_SECURITY_EVENT_CHAL_SENT, + .common.version = AST_SECURITY_EVENT_CHAL_SENT_VERSION, + .common.service = "SIP", + .common.account_id = account_id, + .common.local_addr = { + .sin = security_event_encode_sin_local(p, &sin_local), + .transport = security_event_get_transport(p) + }, + .common.remote_addr = { + .sin = security_event_encode_sin_remote(p, &sin_remote), + .transport = security_event_get_transport(p) + }, + .common.session_id = session_id, + + .challenge = p->randdata, + }; + + if (!ast_strlen_zero(p->from)) { /* When dialing, show account making call */ + ast_copy_string(account_id, p->from, sizeof(account_id)); + } else { + ast_copy_string(account_id, p->exten, sizeof(account_id)); + } + + snprintf(session_id, sizeof(session_id), "%p", p); + + ast_security_event_report(AST_SEC_EVT(&chal_sent)); +} + +void sip_report_inval_transport(const struct sip_pvt *p, const char *transport) +{ + char session_id[32]; + struct sockaddr_in sin_local; + struct sockaddr_in sin_remote; + + struct ast_security_event_inval_transport inval_transport = { + .common.event_type = AST_SECURITY_EVENT_INVAL_TRANSPORT, + .common.version = AST_SECURITY_EVENT_INVAL_TRANSPORT_VERSION, + .common.service = "SIP", + .common.account_id = p->exten, + .common.local_addr = { + .sin = security_event_encode_sin_local(p, &sin_local), + .transport = security_event_get_transport(p) + }, + .common.remote_addr = { + .sin = security_event_encode_sin_remote(p, &sin_remote), + .transport = security_event_get_transport(p) + }, + .common.session_id = session_id, + + .transport = transport, + }; + + snprintf(session_id, sizeof(session_id), "%p", p); + + ast_security_event_report(AST_SEC_EVT(&inval_transport)); +} + +int sip_report_security_event(const struct sip_pvt *p, const struct sip_request *req, const int res) { + + struct sip_peer *peer_report; + enum check_auth_result res_report = res; + struct ast_str *buf; + char *c; + const char *authtoken; + char *reqheader, *respheader; + int result = 0; + char aclname[256]; + struct digestkeys keys[] = { + [K_RESP] = { "response=", "" }, + [K_URI] = { "uri=", "" }, + [K_USER] = { "username=", "" }, + [K_NONCE] = { "nonce=", "" }, + [K_LAST] = { NULL, NULL} + }; + + peer_report = sip_find_peer(p->exten, NULL, TRUE, FINDPEERS, FALSE, 0); + + switch(res_report) { + case AUTH_DONT_KNOW: + break; + case AUTH_SUCCESSFUL: + if (peer_report) { + if (ast_strlen_zero(peer_report->secret) && ast_strlen_zero(peer_report->md5secret)) { + sip_report_auth_success(p, (uint32_t *) 0); + } else { + sip_report_auth_success(p, (uint32_t *) 1); + } + } + break; + case AUTH_CHALLENGE_SENT: + sip_report_chal_sent(p); + break; + case AUTH_SECRET_FAILED: + case AUTH_USERNAME_MISMATCH: + sip_auth_headers(WWW_AUTH, &respheader, &reqheader); + authtoken = sip_get_header(req, reqheader); + buf = ast_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN); + ast_str_set(&buf, 0, "%s", authtoken); + c = buf->str; + + sip_digest_parser(c, keys); + + if (res_report == AUTH_SECRET_FAILED) { + sip_report_inval_password(p, keys[K_NONCE].s, keys[K_RESP].s); + } else { + if (peer_report) { + sip_report_failed_challenge_response(p, keys[K_USER].s, peer_report->username); + } + } + break; + case AUTH_NOT_FOUND: + /* with sip_cfg.alwaysauthreject on, generates 2 events */ + sip_report_invalid_peer(p); + break; + case AUTH_FAKE_AUTH: + break; + case AUTH_UNKNOWN_DOMAIN: + snprintf(aclname, sizeof(aclname), "domain_must_match"); + sip_report_failed_acl(p, aclname); + break; + case AUTH_PEER_NOT_DYNAMIC: + snprintf(aclname, sizeof(aclname), "peer_not_dynamic"); + sip_report_failed_acl(p, aclname); + break; + case AUTH_ACL_FAILED: + /* with sip_cfg.alwaysauthreject on, generates 2 events */ + snprintf(aclname, sizeof(aclname), "device_must_match_acl"); + sip_report_failed_acl(p, aclname); + break; + case AUTH_BAD_TRANSPORT: + sip_report_inval_transport(p, sip_get_transport(req->socket.type)); + break; + case AUTH_RTP_FAILED: + break; + case AUTH_SESSION_LIMIT: + sip_report_session_limit(p); + break; + } + + if (peer_report) { + sip_unref_peer(peer_report, "sip_report_security_event: sip_unref_peer: from handle_incoming"); + } + + return result; +} + |