/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2012, Digium, Inc. * * Michael L. Young * * 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 */ /*** MODULEINFO extended ***/ #include "asterisk.h" #include "include/sip.h" #include "include/security_events.h" /*! \brief Determine transport type used to receive request*/ static enum ast_transport security_event_get_transport(const struct sip_pvt *p) { return p->socket.type; } void sip_report_invalid_peer(const struct sip_pvt *p) { 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 = "SIP", .common.account_id = p->exten, .common.local_addr = { .addr = &p->ourip, .transport = security_event_get_transport(p) }, .common.remote_addr = { .addr = &p->sa, .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 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 = { .addr = &p->ourip, .transport = security_event_get_transport(p) }, .common.remote_addr = { .addr = &p->sa, .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 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 = { .addr = &p->ourip, .transport = security_event_get_transport(p) }, .common.remote_addr = { .addr = &p->sa, .transport = security_event_get_transport(p) }, .common.session_id = session_id, .challenge = p->nonce, .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 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 = { .addr = &p->ourip, .transport = security_event_get_transport(p) }, .common.remote_addr = { .addr = &p->sa, .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 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 = { .addr = &p->ourip, .transport = security_event_get_transport(p) }, .common.remote_addr = { .addr = &p->sa, .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]; 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 = { .addr = &p->ourip, .transport = security_event_get_transport(p) }, .common.remote_addr = { .addr = &p->sa, .transport = security_event_get_transport(p) }, .common.session_id = session_id, .challenge = p->nonce, .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]; 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 = { .addr = &p->ourip, .transport = security_event_get_transport(p) }, .common.remote_addr = { .addr = &p->sa, .transport = security_event_get_transport(p) }, .common.session_id = session_id, .challenge = p->nonce, }; 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 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 = { .addr = &p->ourip, .transport = security_event_get_transport(p) }, .common.remote_addr = { .addr = &p->sa, .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 char *peer, struct ast_sockaddr *addr, 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(peer, addr, TRUE, FINDPEERS, FALSE, p->socket.type); 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, 0); } else { sip_report_auth_success(p, 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 = ast_str_buffer(buf); 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_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; }