ims_ipsec_pcscf: free memory on errors and variable supported
[sip-router] / src / modules / ims_ipsec_pcscf / cmd.c
1 /*
2  * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
3  * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
4  *
5  * The initial version of this code was written by Dragos Vingarzan
6  * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
7  * Fruanhofer Institute. It was and still is maintained in a separate
8  * branch of the original SER. We are therefore migrating it to
9  * Kamailio/SR and look forward to maintaining it from here on out.
10  * 2011/2012 Smile Communications, Pty. Ltd.
11  * ported/maintained/improved by
12  * Jason Penton (jason(dot)penton(at)smilecoms.com and
13  * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
14  * effort to add full IMS support to Kamailio/SR using a new and
15  * improved architecture
16  *
17  * NB: Alot of this code was originally part of OpenIMSCore,
18  * FhG Fokus.
19  * Copyright (C) 2004-2006 FhG Fokus
20  * Thanks for great work! This is an effort to
21  * break apart the various CSCF functions into logically separate
22  * components. We hope this will drive wider use. We also feel
23  * that in this way the architecture is more complete and thereby easier
24  * to manage in the Kamailio/SR environment
25  *
26  * This file is part of Kamailio, a free SIP server.
27  *
28  * Kamailio is free software; you can redistribute it and/or modify
29  * it under the terms of the GNU General Public License as published by
30  * the Free Software Foundation; either version 2 of the License, or
31  * (at your option) any later version
32  *
33  * Kamailio is distributed in the hope that it will be useful,
34  * but WITHOUT ANY WARRANTY; without even the implied warranty of
35  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36  * GNU General Public License for more details.
37  *
38  * You should have received a copy of the GNU General Public License
39  * along with this program; if not, write to the Free Software
40  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
41  *
42  */
43
44 #include "../../core/parser/msg_parser.h"
45 #include "../../core/socket_info.h"
46 #include "../../lib/ims/ims_getters.h"
47 #include "../../modules/tm/tm_load.h"
48 #include "../ims_usrloc_pcscf/usrloc.h"
49
50 #include "ipsec.h"
51 #include "spi_gen.h"
52
53 #include <stdlib.h>
54 #include <string.h>
55
56
57 extern str ipsec_listen_addr;
58 extern short ipsec_listen_port;
59 extern short ipsec_server_port;
60 extern short ipsec_client_port;
61
62 // check http://www.asipto.com/pub/kamailio-devel-guide/#c16return_values
63 const int IPSEC_CMD_FAIL = -1;
64 const int IPSEC_CMD_SUCCESS = 1;
65
66 extern usrloc_api_t ul;
67 extern struct tm_binds tmb;
68
69
70 static str get_www_auth_param(const char* param_name, str www_auth)
71 {
72     str val = {0,0};
73     int i = 0;
74     int param_len = strlen(param_name);
75     int start = 0;
76     int end = 0;
77
78     for(i = 0; i < www_auth.len; i++) {
79         if (www_auth.s[i] == ' ') continue;
80
81         if(strncasecmp(www_auth.s+i, param_name, param_len) == 0) {
82             i += param_len;
83
84             //find first double quote
85             while(www_auth.s[i] != '"' && i < www_auth.len) i++;
86             i++; //and skip it
87
88             if (i == www_auth.len)
89                 return val; //error
90             start = i;
91             i++;
92
93             //find second double quote
94             while(www_auth.s[i] != '"' && i < www_auth.len) i++;
95             if (i == www_auth.len)
96                 return val; //error
97             end = i;
98             i++;
99
100             val.s = www_auth.s + start;
101             val.len = end - start;
102             break;
103         }
104
105         //parameter not relevant - fast forward
106         do { i++; } while (www_auth.s[i] != ',' && i < www_auth.len);
107     }
108
109     return val;
110 }
111
112
113 static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
114 {
115     contact_body_t* cb = NULL;
116     struct via_body* vb = NULL;
117     unsigned short port, proto = 0;
118     struct sip_msg* req = NULL;
119
120
121     if(!ci) {
122         LM_ERR("fill_contact() called with null ptr\n");
123         return -1;
124     }
125
126     memset(ci, 0, sizeof(struct pcontact_info));
127
128
129     if(m->first_line.type == SIP_REQUEST) {
130         struct sip_uri uri;
131         memset(&uri, 0, sizeof(struct sip_uri));
132
133         if(parse_uri(m->first_line.u.request.uri.s, m->first_line.u.request.uri.len, &uri)) {
134             LM_ERR("Can't parse the request URI from first line\n");
135             return -1;
136         }
137
138         // populate host,port, aor in CI
139         ci->via_host = uri.host;
140         ci->via_port = uri.port_no ? uri.port_no : 5060;
141         ci->via_prot = proto;
142         ci->aor = m->first_line.u.request.uri;
143
144         req = m;
145     }
146     else if(m->first_line.type == SIP_REPLY) {
147
148         cb = cscf_parse_contacts(m);
149         vb = cscf_get_ue_via(m);
150         port = vb->port?vb->port:5060;
151         proto = vb->proto;
152
153         struct cell *t = tmb.t_gett();
154         if (!t || t == (void*) -1) {
155             LM_ERR("fill_contact(): Reply without transaction\n");
156             return -1;
157         }
158
159         req = t->uas.request;
160
161         cb = cscf_parse_contacts(req);
162         if (!cb || (!cb->contacts)) {
163             LM_ERR("fill_contact(): No contact headers\n");
164             return -1;
165         }
166
167         // populate CI with bare minimum
168         ci->via_host = vb->host;
169         ci->via_port = port;
170         ci->via_prot = proto;
171         ci->aor = cb->contacts->uri;
172     }
173     else {
174         LM_ERR("fill_contact(): Unknown first line type: %d\n", m->first_line.type);
175         return -1;
176     }
177
178
179     char* srcip = NULL;
180     if((srcip = pkg_malloc(50)) == NULL) {
181         LM_ERR("Error allocating memory for source IP address\n");
182         return -1;
183     }
184
185     ci->received_host.len = ip_addr2sbuf(&req->rcv.src_ip, srcip, 50);
186     ci->received_host.s = srcip;
187     ci->received_port = req->rcv.src_port;
188     ci->received_proto = req->rcv.proto;
189
190     // Set to default, if not set:
191     if (ci->received_port == 0)
192         ci->received_port = 5060;
193
194
195     return 0;
196 }
197
198 // Get CK and IK from WWW-Authenticate
199 static int get_ck_ik(const struct sip_msg* m, str* ck, str* ik)
200 {
201     struct hdr_field *www_auth_hdr = NULL;
202     str www_auth;
203     memset(&www_auth, 0, sizeof(str));
204
205     www_auth = cscf_get_authenticate((sip_msg_t*)m, &www_auth_hdr);
206
207     *ck = get_www_auth_param("ck", www_auth);
208     if (ck->len == 0) {
209         LM_ERR("Error getting CK\n");
210         return -1;
211     }
212
213     *ik = get_www_auth_param("ik", www_auth);
214     if (ck->len == 0) {
215         LM_ERR("Error getting IK\n");
216         return -1;
217     }
218
219     return 0;
220 }
221
222 static int update_contact_ipsec_params(ipsec_t* s, const struct sip_msg* m)
223 {
224     // Get CK and IK
225     str ck, ik;
226     if(get_ck_ik(m, &ck, &ik) != 0) {
227         return -1;
228     }
229
230     // Save CK and IK in the contact
231     s->ck.s = shm_malloc(ck.len);
232     if(s->ck.s == NULL) {
233         LM_ERR("Error allocating memory for CK\n");
234         return -1;
235     }
236     memcpy(s->ck.s, ck.s, ck.len);
237     s->ck.len = ck.len;
238
239     s->ik.s = shm_malloc(ik.len);
240     if(s->ik.s == NULL) {
241         LM_ERR("Error allocating memory for IK\n");
242         shm_free(s->ck.s);
243         s->ck.s = NULL; s->ck.len = 0;
244         s->ik.s = NULL; s->ik.len = 0;
245         return -1;
246     }
247     memcpy(s->ik.s, ik.s, ik.len);
248     s->ik.len = ik.len;
249
250     // Generate SPI
251     if((s->spi_pc = acquire_spi()) == 0) {
252         LM_ERR("Error generating client SPI for IPSEC tunnel creation\n");
253         shm_free(s->ck.s);
254         s->ck.s = NULL; s->ck.len = 0;
255         shm_free(s->ik.s);
256         s->ik.s = NULL; s->ik.len = 0;
257         return -1;
258     }
259
260     if((s->spi_ps = acquire_spi()) == 0) {
261         LM_ERR("Error generating server SPI for IPSEC tunnel creation\n");
262         shm_free(s->ck.s);
263         s->ck.s = NULL; s->ck.len = 0;
264         shm_free(s->ik.s);
265         s->ik.s = NULL; s->ik.len = 0;
266         return -1;
267     }
268
269     return 0;
270 }
271
272 static int create_ipsec_tunnel(const str remote_addr, ipsec_t* s)
273 {
274     struct mnl_socket* sock = init_mnl_socket();
275     if (sock == NULL) {
276         return -1;
277     }
278
279     LM_DBG("Creating security associations: Local IP: %.*s client port: %d server port: %d; UE IP: %.*s; client port %d server port %d\n",
280             ipsec_listen_addr.len, ipsec_listen_addr.s, ipsec_client_port, ipsec_server_port,
281             remote_addr.len, remote_addr.s, s->port_uc, s->port_us);
282
283     // P-CSCF 'client' tunnel to UE 'server'
284     add_sa    (sock, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, s->ck, s->ik);
285     add_policy(sock, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT);
286
287     // UE 'client' to P-CSCF 'server' tunnel
288     add_sa    (sock, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps, s->ck, s->ik);
289     add_policy(sock, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps, IPSEC_POLICY_DIRECTION_IN);
290
291     close_mnl_socket(sock);
292
293     return 0;
294 }
295
296 static int destroy_ipsec_tunnel(const str remote_addr, ipsec_t* s)
297 {
298     struct mnl_socket* sock = init_mnl_socket();
299     if (sock == NULL) {
300         return -1;
301     }
302
303     LM_DBG("Destroying security associations: Local IP: %.*s client port: %d server port: %d; UE IP: %.*s; client port %d server port %d\n",
304             ipsec_listen_addr.len, ipsec_listen_addr.s, ipsec_client_port, ipsec_server_port,
305             remote_addr.len, remote_addr.s, s->port_uc, s->port_us);
306
307     // P-CSCF 'client' tunnel to UE 'server'
308     remove_sa    (sock, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us);
309     remove_policy(sock, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT);
310
311     // UE 'client' to P-CSCF 'server' tunnel
312     remove_sa    (sock, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps);
313     remove_policy(sock, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps, IPSEC_POLICY_DIRECTION_IN);
314
315     // Release SPIs
316     release_spi(s->spi_uc);
317     release_spi(s->spi_us);
318
319
320     close_mnl_socket(sock);
321     return 0;
322 }
323
324 static void on_expire(struct pcontact *c, int type, void *param)
325 {
326     if(type != PCSCF_CONTACT_EXPIRE && type != PCSCF_CONTACT_DELETE) {
327         LM_ERR("Unexpected event type %d\n", type);
328         return;
329     }
330
331
332     if(c->security_temp == NULL) {
333         LM_ERR("No security parameters found in contact\n");
334         return;
335     }
336
337     //get security parameters
338     if(c->security_temp->type != SECURITY_IPSEC ) {
339         LM_ERR("Unsupported security type: %d\n", c->security_temp->type);
340         return;
341     }
342
343     destroy_ipsec_tunnel(c->received_host, c->security_temp->data.ipsec);
344 }
345
346 int add_supported_secagree_header(struct sip_msg* m)
347 {
348     // Add sec-agree header in the reply
349     const char* supported_sec_agree = "Supported: sec-agree\r\n";
350     const int supported_sec_agree_len = 22;
351
352     str* supported = NULL;
353     if((supported = pkg_malloc(sizeof(str))) == NULL) {
354         LM_ERR("Error allocating pkg memory for supported header\n");
355         return -1;
356     }
357
358     if((supported->s = pkg_malloc(supported_sec_agree_len)) == NULL) {
359         LM_ERR("Error allcationg pkg memory for supported header str\n");
360         pkg_free(supported);
361         return -1;
362     }
363     memcpy(supported->s, supported_sec_agree, supported_sec_agree_len);
364     supported->len = supported_sec_agree_len;
365
366     if(cscf_add_header(m, supported, HDR_SUPPORTED_T) != 1) {
367                 pkg_free(supported->s);
368                 pkg_free(supported);
369         LM_ERR("Error adding security header to reply!\n");
370         return -1;
371     }
372     pkg_free(supported);
373
374     return 0;
375 }
376
377 int add_security_server_header(struct sip_msg* m, ipsec_t* s)
378 {
379     // allocate memory for the header itself
380     str* sec_header = NULL;
381     if((sec_header = pkg_malloc(sizeof(str))) == NULL) {
382         LM_ERR("Error allocating pkg memory for security header\n");
383         return -1;
384     }
385     memset(sec_header, 0, sizeof(str));
386
387     // create a temporary buffer and set the value in it
388     char sec_hdr_buf[1024];
389     memset(sec_hdr_buf, 0, sizeof(sec_hdr_buf));
390     sec_header->len = snprintf(sec_hdr_buf, sizeof(sec_hdr_buf) - 1,
391                                 "Security-Server: ipsec-3gpp;prot=esp;mod=trans;spi-c=%d;spi-s=%d;port-c=%d;port-s=%d;alg=%.*s;ealg=%.*s\r\n",
392                                 s->spi_pc, s->spi_ps, ipsec_client_port, ipsec_server_port,
393                                 s->r_alg.len, s->r_alg.s,
394                                 s->r_ealg.len, s->r_ealg.s
395                               );
396
397     // copy to the header and add
398     if((sec_header->s = pkg_malloc(sec_header->len)) == NULL) {
399         LM_ERR("Error allocating pkg memory for security header payload\n");
400         pkg_free(sec_header);
401         return -1;
402     }
403     memcpy(sec_header->s, sec_hdr_buf, sec_header->len);
404
405     // add security-server header in reply
406     if(cscf_add_header(m, sec_header, HDR_OTHER_T) != 1) {
407         LM_ERR("Error adding security header to reply!\n");
408         pkg_free(sec_header->s);
409         pkg_free(sec_header);
410         return -1;
411     }
412
413     return 0;
414 }
415
416 int ipsec_create(struct sip_msg* m, udomain_t* d)
417 {
418     pcontact_t* pcontact = NULL;
419     struct pcontact_info ci;
420     int ret = IPSEC_CMD_FAIL;   // FAIL by default
421
422     // Find the contact
423     if(fill_contact(&ci, m) != 0) {
424         LM_ERR("Error filling in contact data\n");
425         return ret;
426     }
427
428     ul.lock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
429
430     if (ul.get_pcontact(d, &ci, &pcontact) != 0) {
431         LM_ERR("Contact doesn't exist\n");
432         goto cleanup;
433     }
434
435     // Get security parameters
436     if(pcontact->security_temp == NULL) {
437         LM_ERR("No security parameters found in contact\n");
438         goto cleanup;
439     }
440
441     if(pcontact->security_temp->type != SECURITY_IPSEC ) {
442         LM_ERR("Unsupported security type: %d\n", pcontact->security_temp->type);
443         goto cleanup;
444     }
445
446     ipsec_t* s = pcontact->security_temp->data.ipsec;
447
448     if(update_contact_ipsec_params(s, m) != 0) {
449         goto cleanup;
450     }
451
452     if(create_ipsec_tunnel(ci.received_host, s) != 0) {
453         goto cleanup;
454     }
455
456     // TODO: Save security_tmp to security!!!!!
457
458     if (ul.update_pcontact(d, &ci, pcontact) != 0) {
459         LM_ERR("Error updating contact\n");
460         goto cleanup;
461     }
462
463     // Destroy the tunnel, if the contact expires
464     if(ul.register_ulcb(pcontact, PCSCF_CONTACT_EXPIRE|PCSCF_CONTACT_DELETE, on_expire, NULL) != 1) {
465         LM_ERR("Error subscribing for contact\n");
466         goto cleanup;
467     }
468
469
470     if(add_supported_secagree_header(m) != 0) {
471         goto cleanup;
472     }
473
474     if(add_security_server_header(m, s) != 0) {
475         goto cleanup;
476     }
477
478     ret = IPSEC_CMD_SUCCESS;    // all good, set ret to SUCCESS, and exit
479
480 cleanup:
481     // Do not free str* sec_header! It will be freed in data_lump.c -> free_lump()
482     ul.unlock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
483     pkg_free(ci.received_host.s);
484     return ret;
485 }
486
487
488 int ipsec_forward(struct sip_msg* m, udomain_t* d)
489 {
490     struct pcontact_info ci;
491     pcontact_t* pcontact = NULL;
492     int ret = IPSEC_CMD_FAIL; // FAIL by default
493
494     //
495     // Find the contact
496     //
497     if(fill_contact(&ci, m) != 0) {
498         LM_ERR("Error filling in contact data\n");
499         return ret;
500     }
501
502     ul.lock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
503
504     if (ul.get_pcontact(d, &ci, &pcontact) != 0) {
505         LM_ERR("Contact doesn't exist\n");
506         goto cleanup;
507     }
508
509
510     if(pcontact->security_temp == NULL) {
511         LM_ERR("No security parameters found in contact\n");
512         goto cleanup;
513     }
514
515     //get security parameters
516     if(pcontact->security_temp->type != SECURITY_IPSEC ) {
517         LM_ERR("Unsupported security type: %d\n", pcontact->security_temp->type);
518         goto cleanup;
519     }
520
521     ipsec_t* s = pcontact->security_temp->data.ipsec;
522
523
524     // Update the destination
525     //
526     //       from sec-agree
527     //            v
528     // sip:host:port
529     //       ^
530     //    from URI
531     //int uri_len = 4 /* strlen("sip:") */ + ci.via_host.len + 5 /* max len of port number */ ;
532
533     if(m->dst_uri.s) {
534         pkg_free(m->dst_uri.s);
535         m->dst_uri.s = NULL;
536         m->dst_uri.len = 0;
537     }
538
539     char buf[1024];
540     int buf_len = snprintf(buf, sizeof(buf) - 1, "sip:%.*s:%d", ci.via_host.len, ci.via_host.s, s->port_us);
541
542     if((m->dst_uri.s = pkg_malloc(buf_len)) == NULL) {
543         LM_ERR("Error allocating memory for dst_uri\n");
544         goto cleanup;
545     }
546
547     memcpy(m->dst_uri.s, buf, buf_len);
548     m->dst_uri.len = buf_len;
549
550     // Set send socket
551     struct socket_info * client_sock = grep_sock_info(&ipsec_listen_addr, ipsec_client_port, PROTO_UDP);
552     if(!client_sock) {
553         LM_ERR("Error calling grep_sock_info() for ipsec client port in ipsec_forward\n");
554         return -1;
555     }
556     m->force_send_socket = client_sock;
557
558    // Set destination info
559     struct dest_info dst_info;
560     dst_info.send_sock = client_sock;
561 #ifdef USE_DNS_FAILOVER
562     if (!uri2dst(NULL, &dst_info, m, &m->dst_uri, PROTO_UDP)) {
563 #else
564     if (!uri2dst(&dst_info, m, &m->dst_uri, PROTO_UDP)) {
565 #endif
566         LM_ERR("Error converting dst_uri (%.*s) to struct dst_info\n", m->dst_uri.len, m->dst_uri.s);
567         goto cleanup;
568     }
569
570     // Update dst_info in message
571     if(m->first_line.type == SIP_REPLY) {
572         struct cell *t = tmb.t_gett();
573         if (!t) {
574             LM_ERR("Error getting transaction\n");
575             goto cleanup;
576         }
577         t->uas.response.dst = dst_info;
578     }
579
580     LM_DBG("Destination changed to %.*s\n", m->dst_uri.len, m->dst_uri.s);
581
582     ret = IPSEC_CMD_SUCCESS; // all good, return SUCCESS
583
584     if(add_supported_secagree_header(m) != 0) {
585         goto cleanup;
586     }
587
588     if(add_security_server_header(m, s) != 0) {
589         goto cleanup;
590     }
591
592     ret = IPSEC_CMD_SUCCESS;    // all good, set ret to SUCCESS, and exit
593
594 cleanup:
595     ul.unlock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
596     pkg_free(ci.received_host.s);
597     return ret;
598 }
599
600
601 int ipsec_destroy(struct sip_msg* m, udomain_t* d)
602 {
603     struct pcontact_info ci;
604     pcontact_t* pcontact = NULL;
605     int ret = IPSEC_CMD_FAIL; // FAIL by default
606
607     //
608     // Find the contact
609     //
610     if(fill_contact(&ci, m) != 0) {
611         LM_ERR("Error filling in contact data\n");
612         return ret;
613     }
614
615     ul.lock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
616
617     if (ul.get_pcontact(d, &ci, &pcontact) != 0) {
618         LM_ERR("Contact doesn't exist\n");
619         goto cleanup;
620     }
621
622
623     if(pcontact->security_temp == NULL) {
624         LM_ERR("No security parameters found in contact\n");
625         goto cleanup;
626     }
627
628     //get security parameters
629     if(pcontact->security_temp->type != SECURITY_IPSEC ) {
630         LM_ERR("Unsupported security type: %d\n", pcontact->security_temp->type);
631         goto cleanup;
632     }
633
634     destroy_ipsec_tunnel(ci.received_host, pcontact->security_temp->data.ipsec);
635
636     ret = IPSEC_CMD_SUCCESS;    // all good, set ret to SUCCESS, and exit
637
638 cleanup:
639     ul.unlock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
640     pkg_free(ci.received_host.s);
641     return ret;
642 }
643
644 int ipsec_cleanall()
645 {
646     struct mnl_socket* nlsock = init_mnl_socket();
647     if(!nlsock) {
648         return -1;
649     }
650
651     if(clean_sa(nlsock) != 0) {
652         LM_WARN("Error cleaning IPSec Security associations during startup.\n");
653     }
654
655     if(clean_policy(nlsock) != 0) {
656         LM_WARN("Error cleaning IPSec Policies during startup.\n");
657     }
658
659     close_mnl_socket(nlsock);
660
661     return 0;
662 }