7b4913753b089298dbef1d1e32548ea1d4f1ae78
[asterisk/asterisk.git] / res / res_pjsip / security_events.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  *
22  * \brief Generate security events in the PJSIP channel
23  *
24  * \author Joshua Colp <jcolp@digium.com>
25  */
26
27 #include "asterisk.h"
28
29 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30
31 #include <pjsip.h>
32
33 #include "asterisk/res_pjsip.h"
34 #include "asterisk/security_events.h"
35
36 static int find_transport_in_use(void *obj, void *arg, int flags)
37 {
38         struct ast_sip_transport *transport = obj;
39         pjsip_rx_data *rdata = arg;
40
41         if ((transport->state->transport == rdata->tp_info.transport) ||
42                 (transport->state->factory && !pj_strcmp(&transport->state->factory->addr_name.host, &rdata->tp_info.transport->local_name.host) &&
43                         transport->state->factory->addr_name.port == rdata->tp_info.transport->local_name.port)) {
44                 return CMP_MATCH | CMP_STOP;
45         }
46
47         return 0;
48 }
49
50 static enum ast_transport security_event_get_transport(pjsip_rx_data *rdata)
51 {
52         RAII_VAR(struct ao2_container *, transports, NULL, ao2_cleanup);
53         RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
54
55         /* It should be impossible for these to fail as the transport has to exist for the message to exist */
56         transports = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
57
58         ast_assert(transports != NULL);
59
60         transport = ao2_callback(transports, 0, find_transport_in_use, rdata);
61
62         ast_assert(transport != NULL);
63
64         return transport->type;
65 }
66
67 static void security_event_populate(pjsip_rx_data *rdata, char *call_id, size_t call_id_size, struct ast_sockaddr *local, struct ast_sockaddr *remote)
68 {
69         char host[NI_MAXHOST];
70
71         ast_copy_pj_str(call_id, &rdata->msg_info.cid->id, call_id_size);
72
73         ast_copy_pj_str(host, &rdata->tp_info.transport->local_name.host, sizeof(host));
74         ast_sockaddr_parse(local, host, PARSE_PORT_FORBID);
75         ast_sockaddr_set_port(local, rdata->tp_info.transport->local_name.port);
76
77         ast_sockaddr_parse(remote, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
78         ast_sockaddr_set_port(remote, rdata->pkt_info.src_port);
79 }
80
81 void ast_sip_report_invalid_endpoint(const char *name, pjsip_rx_data *rdata)
82 {
83         enum ast_transport transport = security_event_get_transport(rdata);
84         char call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
85         struct ast_sockaddr local, remote;
86
87         struct ast_security_event_inval_acct_id inval_acct_id = {
88                 .common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID,
89                 .common.version    = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
90                 .common.service    = "PJSIP",
91                 .common.account_id = name,
92                 .common.local_addr = {
93                         .addr      = &local,
94                         .transport = transport,
95                 },
96                 .common.remote_addr = {
97                         .addr       = &remote,
98                         .transport = transport,
99                 },
100                 .common.session_id = call_id,
101         };
102
103         security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
104
105         ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
106 }
107
108 void ast_sip_report_failed_acl(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *name)
109 {
110         enum ast_transport transport = security_event_get_transport(rdata);
111         char call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
112         struct ast_sockaddr local, remote;
113
114         struct ast_security_event_failed_acl failed_acl_event = {
115                         .common.event_type  = AST_SECURITY_EVENT_FAILED_ACL,
116                         .common.version     = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
117                         .common.service     = "PJSIP",
118                         .common.account_id  = ast_sorcery_object_get_id(endpoint),
119                         .common.local_addr  = {
120                                         .addr       = &local,
121                                         .transport  = transport,
122                         },
123                         .common.remote_addr = {
124                                         .addr       = &remote,
125                                         .transport  = transport,
126                         },
127                         .common.session_id  = call_id,
128                         .acl_name           = name,
129         };
130
131         security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
132
133         ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
134 }
135
136 void ast_sip_report_auth_failed_challenge_response(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
137 {
138         pjsip_authorization_hdr *auth = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, NULL);
139         enum ast_transport transport = security_event_get_transport(rdata);
140         char call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
141         char nonce[64] = "", response[256] = "";
142         struct ast_sockaddr local, remote;
143
144         struct ast_security_event_chal_resp_failed chal_resp_failed = {
145                                 .common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED,
146                                 .common.version    = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
147                                 .common.service    = "PJSIP",
148                                 .common.account_id = ast_sorcery_object_get_id(endpoint),
149                                 .common.local_addr = {
150                                                 .addr      = &local,
151                                                 .transport = transport,
152                                 },
153                                 .common.remote_addr = {
154                                                 .addr      = &remote,
155                                                 .transport = transport,
156                                 },
157                                 .common.session_id = call_id,
158
159                                 .challenge         = nonce,
160                                 .response          = response,
161                                 .expected_response = "",
162                 };
163
164         if (auth && !pj_strcmp2(&auth->scheme, "digest")) {
165                 ast_copy_pj_str(nonce, &auth->credential.digest.nonce, sizeof(nonce));
166                 ast_copy_pj_str(response, &auth->credential.digest.response, sizeof(response));
167         }
168
169         security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
170
171         ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
172 }
173
174 void ast_sip_report_auth_success(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
175 {
176         pjsip_authorization_hdr *auth = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, NULL);
177         enum ast_transport transport = security_event_get_transport(rdata);
178         char call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
179         struct ast_sockaddr local, remote;
180
181         struct ast_security_event_successful_auth successful_auth = {
182                         .common.event_type  = AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
183                         .common.version     = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
184                         .common.service     = "PJSIP",
185                         .common.account_id  = ast_sorcery_object_get_id(endpoint),
186                         .common.local_addr  = {
187                                         .addr       = &local,
188                                         .transport  = transport,
189                         },
190                         .common.remote_addr = {
191                                         .addr       = &remote,
192                                         .transport  = transport,
193                         },
194                         .common.session_id  = call_id,
195                         .using_password     = auth ? (uint32_t *)1 : (uint32_t *)0,
196         };
197
198         security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
199
200         ast_security_event_report(AST_SEC_EVT(&successful_auth));
201 }
202
203 void ast_sip_report_auth_challenge_sent(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pjsip_tx_data *tdata)
204 {
205         pjsip_www_authenticate_hdr *auth = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_WWW_AUTHENTICATE, NULL);
206         enum ast_transport transport = security_event_get_transport(rdata);
207         char nonce[64] = "", call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
208         struct ast_sockaddr local, remote;
209
210         struct ast_security_event_chal_sent chal_sent = {
211                         .common.event_type = AST_SECURITY_EVENT_CHAL_SENT,
212                         .common.version    = AST_SECURITY_EVENT_CHAL_SENT_VERSION,
213                         .common.service    = "PJSIP",
214                         .common.account_id = ast_sorcery_object_get_id(endpoint),
215                         .common.local_addr = {
216                                         .addr      = &local,
217                                         .transport = transport,
218                         },
219                         .common.remote_addr = {
220                                         .addr      = &remote,
221                                         .transport = transport,
222                         },
223                         .common.session_id = call_id,
224                         .challenge         = nonce,
225         };
226
227         if (auth && !pj_strcmp2(&auth->scheme, "digest")) {
228                 ast_copy_pj_str(nonce, &auth->challenge.digest.nonce, sizeof(nonce));
229         }
230
231         security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
232
233         ast_security_event_report(AST_SEC_EVT(&chal_sent));
234 }