summaryrefslogtreecommitdiff
path: root/channels/sip
diff options
context:
space:
mode:
Diffstat (limited to 'channels/sip')
-rw-r--r--channels/sip/include/security_events.h43
-rw-r--r--channels/sip/include/sip.h46
-rw-r--r--channels/sip/security_events.c398
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;
+}
+