presence_xml: use common prefix for global variables
[sip-router] / src / modules / presence_xml / xcap_auth.c
1 /*
2  * presence_xml module
3  *
4  * Copyright (C) 2007 Voice Sistem S.R.L.
5  *
6  * This file is part of Kamailio, a free SIP server.
7  *
8  * Kamailio is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * Kamailio is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 /*! \file
25  * \brief Kamailio Presence_XML :: XCAP authentication
26  * \ingroup presence_xml
27  */
28
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
34 #include <libxml/parser.h>
35
36 #include "../../core/str.h"
37 #include "../../core/dprint.h"
38 #include "../../core/parser/parse_uri.h"
39 #include "../presence/utils_func.h"
40 #include "../presence/hash.h"
41 #include "presence_xml.h"
42 #include "xcap_auth.h"
43 #include "pidf.h"
44
45 extern str xcapauth_userdel_reason;
46
47 int http_get_rules_doc(str user, str domain, str *rules_doc);
48
49 int pres_watcher_allowed(subs_t *subs)
50 {
51         xmlDocPtr xcap_tree = NULL;
52         xmlNodePtr node = NULL, actions_node = NULL;
53         xmlNodePtr sub_handling_node = NULL;
54         char *sub_handling = NULL;
55         int ret = 0;
56
57         /* if pxml_force_active set status to active*/
58         if(pxml_force_active) {
59                 subs->status = ACTIVE_STATUS;
60                 subs->reason.s = NULL;
61                 subs->reason.len = 0;
62                 return 0;
63         }
64
65         if(subs->auth_rules_doc == NULL) {
66                 subs->status = PENDING_STATUS;
67                 subs->reason.s = NULL;
68                 subs->reason.len = 0;
69                 return 0;
70         }
71
72         xcap_tree =
73                         xmlParseMemory(subs->auth_rules_doc->s, subs->auth_rules_doc->len);
74         if(xcap_tree == NULL) {
75                 LM_ERR("parsing xml memory\n");
76                 return -1;
77         }
78
79         node = get_rule_node(subs, xcap_tree);
80         if(node == NULL) {
81                 /* if no rule node was found and the previous state was active -> set the
82                  * state to terminated with reason xcapauth_userdel_reason (default "probation") */
83                 if(subs->status != PENDING_STATUS) {
84                         subs->status = TERMINATED_STATUS;
85                         subs->reason = xcapauth_userdel_reason;
86                 }
87                 goto done;
88         }
89
90         subs->status = PENDING_STATUS;
91         subs->reason.s = NULL;
92         subs->reason.len = 0;
93
94         /* process actions */
95         actions_node = xmlNodeGetChildByName(node, "actions");
96         if(actions_node == NULL) {
97                 LM_DBG("actions_node NULL\n");
98                 goto done;
99         }
100         LM_DBG("actions_node->name= %s\n", actions_node->name);
101
102         sub_handling_node = xmlNodeGetChildByName(actions_node, "sub-handling");
103         if(sub_handling_node == NULL) {
104                 LM_DBG("sub_handling_node NULL\n");
105                 goto done;
106         }
107         sub_handling = (char *)xmlNodeGetContent(sub_handling_node);
108         LM_DBG("sub_handling_node->name= %s\n", sub_handling_node->name);
109         LM_DBG("sub_handling_node->content= %s\n", sub_handling);
110
111         if(sub_handling == NULL) {
112                 LM_ERR("Couldn't get sub-handling content\n");
113                 ret = -1;
114                 goto done;
115         }
116         if(strncmp((char *)sub_handling, "block", 5) == 0) {
117                 subs->status = TERMINATED_STATUS;
118                 subs->reason.s = "rejected";
119                 subs->reason.len = 8;
120         } else if(strncmp((char *)sub_handling, "confirm", 7) == 0) {
121                 subs->status = PENDING_STATUS;
122         } else if(strncmp((char *)sub_handling, "polite-block", 12) == 0) {
123                 subs->status = ACTIVE_STATUS;
124                 subs->reason.s = "polite-block";
125                 subs->reason.len = 12;
126         } else if(strncmp((char *)sub_handling, "allow", 5) == 0) {
127                 subs->status = ACTIVE_STATUS;
128         } else {
129                 LM_ERR("unknown subscription handling action\n");
130                 ret = -1;
131         }
132
133 done:
134         if(sub_handling)
135                 xmlFree(sub_handling);
136         xmlFreeDoc(xcap_tree);
137         return ret;
138 }
139
140 xmlNodePtr get_rule_node(subs_t *subs, xmlDocPtr xcap_tree)
141 {
142         str w_uri = {0, 0};
143         char *id = NULL, *domain = NULL, *time_cont = NULL;
144         int apply_rule = -1;
145         xmlNodePtr ruleset_node = NULL, node1 = NULL, node2 = NULL;
146         xmlNodePtr cond_node = NULL, except_node = NULL;
147         xmlNodePtr identity_node = NULL, sphere_node = NULL;
148         xmlNodePtr iden_child;
149         xmlNodePtr validity_node, time_node;
150         time_t t_init, t_fin, t;
151         int valid = 0;
152
153
154         uandd_to_uri(subs->watcher_user, subs->watcher_domain, &w_uri);
155         if(w_uri.s == NULL) {
156                 LM_ERR("while creating uri\n");
157                 return NULL;
158         }
159         ruleset_node = xmlDocGetNodeByName(xcap_tree, "ruleset", NULL);
160         if(ruleset_node == NULL) {
161                 LM_DBG("ruleset_node NULL\n");
162                 goto error;
163         }
164         for(node1 = ruleset_node->children; node1; node1 = node1->next) {
165                 if(xmlStrcasecmp(node1->name, (unsigned char *)"text") == 0)
166                         continue;
167
168                 /* process conditions */
169                 LM_DBG("node1->name= %s\n", node1->name);
170
171                 cond_node = xmlNodeGetChildByName(node1, "conditions");
172                 if(cond_node == NULL) {
173                         LM_DBG("cond node NULL\n");
174                         goto error;
175                 }
176                 LM_DBG("cond_node->name= %s\n", cond_node->name);
177
178                 validity_node = xmlNodeGetChildByName(cond_node, "validity");
179                 if(validity_node != NULL) {
180                         LM_DBG("found validity tag\n");
181
182                         t = time(NULL);
183
184                         /* search all from-until pair */
185                         for(time_node = validity_node->children; time_node;
186                                         time_node = time_node->next) {
187                                 if(xmlStrcasecmp(time_node->name, (unsigned char *)"from")
188                                                 != 0) {
189                                         continue;
190                                 }
191                                 time_cont = (char *)xmlNodeGetContent(time_node);
192                                 t_init = xml_parse_dateTime(time_cont);
193                                 xmlFree(time_cont);
194                                 if(t_init < 0) {
195                                         LM_ERR("failed to parse xml dateTime\n");
196                                         goto error;
197                                 }
198
199                                 if(t < t_init) {
200                                         LM_DBG("the lower time limit is not respected\n");
201                                         continue;
202                                 }
203
204                                 time_node = time_node->next;
205                                 while(1) {
206                                         if(time_node == NULL) {
207                                                 LM_ERR("bad formatted xml doc:until child not found in"
208                                                            " validity pair\n");
209                                                 goto error;
210                                         }
211                                         if(xmlStrcasecmp(time_node->name, (unsigned char *)"until")
212                                                         == 0)
213                                                 break;
214                                         time_node = time_node->next;
215                                 }
216
217                                 time_cont = (char *)xmlNodeGetContent(time_node);
218                                 t_fin = xml_parse_dateTime(time_cont);
219                                 xmlFree(time_cont);
220
221                                 if(t_fin < 0) {
222                                         LM_ERR("failed to parse xml dateTime\n");
223                                         goto error;
224                                 }
225
226                                 if(t <= t_fin) {
227                                         LM_DBG("the rule is active at this time\n");
228                                         valid = 1;
229                                 }
230                         }
231
232                         if(!valid) {
233                                 LM_DBG("the rule is not active at this time\n");
234                                 continue;
235                         }
236                 }
237
238                 sphere_node = xmlNodeGetChildByName(cond_node, "sphere");
239                 if(sphere_node != NULL) {
240                         /* check to see if matches presentity current sphere */
241                         /* ask presence for sphere information */
242
243                         char *sphere = psapi.get_sphere(&subs->pres_uri);
244                         if(sphere) {
245                                 char *attr = (char *)xmlNodeGetContent(sphere_node);
246                                 if(xmlStrcasecmp((unsigned char *)attr, (unsigned char *)sphere)
247                                                 != 0) {
248                                         LM_DBG("sphere condition not respected\n");
249                                         pkg_free(sphere);
250                                         xmlFree(attr);
251                                         continue;
252                                 }
253                                 pkg_free(sphere);
254                                 xmlFree(attr);
255                         }
256
257                         /* if the user has not define a sphere ->
258                          *                                              consider the condition true*/
259                 }
260
261                 identity_node = xmlNodeGetChildByName(cond_node, "identity");
262                 if(identity_node == NULL) {
263                         LM_WARN("didn't find identity tag\n");
264                         continue;
265                 }
266
267                 iden_child = xmlNodeGetChildByName(identity_node, "one");
268                 if(iden_child) {
269                         for(node2 = identity_node->children; node2; node2 = node2->next) {
270                                 if(xmlStrcasecmp(node2->name, (unsigned char *)"one") != 0)
271                                         continue;
272
273                                 id = xmlNodeGetAttrContentByName(node2, "id");
274                                 if(id == NULL) {
275                                         LM_ERR("while extracting attribute\n");
276                                         goto error;
277                                 }
278                                 if((strlen(id) == w_uri.len
279                                                    && (strncmp(id, w_uri.s, w_uri.len) == 0))) {
280                                         apply_rule = 1;
281                                         xmlFree(id);
282                                         break;
283                                 }
284                                 xmlFree(id);
285                         }
286                 }
287
288                 /* search for many node*/
289                 iden_child = xmlNodeGetChildByName(identity_node, "many");
290                 if(iden_child) {
291                         domain = NULL;
292                         for(node2 = identity_node->children; node2; node2 = node2->next) {
293                                 if(xmlStrcasecmp(node2->name, (unsigned char *)"many") != 0)
294                                         continue;
295
296                                 domain = xmlNodeGetAttrContentByName(node2, "domain");
297                                 if(domain == NULL) {
298                                         LM_DBG("No domain attribute to many\n");
299                                 } else {
300                                         LM_DBG("<many domain= %s>\n", domain);
301                                         if((strlen(domain) != subs->from_domain.len
302                                                            && strncmp(domain, subs->from_domain.s,
303                                                                                   subs->from_domain.len))) {
304                                                 xmlFree(domain);
305                                                 continue;
306                                         }
307                                 }
308                                 xmlFree(domain);
309                                 apply_rule = 1;
310                                 if(node2->children == NULL) /* there is no exception */
311                                         break;
312
313                                 for(except_node = node2->children; except_node;
314                                                 except_node = except_node->next) {
315                                         if(xmlStrcasecmp(
316                                                            except_node->name, (unsigned char *)"except"))
317                                                 continue;
318
319                                         id = xmlNodeGetAttrContentByName(except_node, "id");
320                                         if(id != NULL) {
321                                                 if((strlen(id) - 1 == w_uri.len
322                                                                    && (strncmp(id, w_uri.s, w_uri.len) == 0))) {
323                                                         xmlFree(id);
324                                                         apply_rule = 0;
325                                                         break;
326                                                 }
327                                                 xmlFree(id);
328                                         } else {
329                                                 domain = NULL;
330                                                 domain = xmlNodeGetAttrContentByName(
331                                                                 except_node, "domain");
332                                                 if(domain != NULL) {
333                                                         LM_DBG("Found except domain= %s\n- strlen(domain)= "
334                                                                    "%d\n",
335                                                                         domain, (int)strlen(domain));
336                                                         if(strlen(domain) == subs->from_domain.len
337                                                                         && (strncmp(domain, subs->from_domain.s,
338                                                                                                 subs->from_domain.len)
339                                                                                            == 0)) {
340                                                                 LM_DBG("except domain match\n");
341                                                                 xmlFree(domain);
342                                                                 apply_rule = 0;
343                                                                 break;
344                                                         }
345                                                         xmlFree(domain);
346                                                 }
347                                         }
348                                 }
349                                 if(apply_rule
350                                                 == 1) /* if a match was found no need to keep searching*/
351                                         break;
352                         }
353                 }
354                 if(apply_rule == 1)
355                         break;
356         }
357
358         LM_DBG("apply_rule= %d\n", apply_rule);
359         if(w_uri.s != NULL)
360                 pkg_free(w_uri.s);
361
362         if(!apply_rule || !node1)
363                 return NULL;
364
365         return node1;
366
367 error:
368         if(w_uri.s)
369                 pkg_free(w_uri.s);
370         return NULL;
371 }
372
373 int pres_get_rules_doc(str *user, str *domain, str **rules_doc)
374 {
375
376         return get_rules_doc(user, domain, NULL, PRES_RULES, rules_doc);
377 }
378
379 int pres_get_pidf_doc(str *user, str *domain, str *file_uri, str **rules_doc)
380 {
381         return get_rules_doc(user, domain, file_uri, PIDF_MANIPULATION, rules_doc);
382 }
383
384 int get_rules_doc(
385                 str *user, str *domain, str *file_uri, int type, str **rules_doc)
386 {
387         db_key_t query_cols[3];
388         db_val_t query_vals[3];
389         db_key_t result_cols[1];
390         int n_query_cols = 0;
391         db1_res_t *result = 0;
392         db_row_t *row;
393         db_val_t *row_vals;
394         str body;
395         str *doc = NULL;
396         int n_result_cols = 0, xcap_doc_col;
397         static str tmp1 = str_init("doc_type");
398         static str tmp2 = str_init("doc_uri");
399         static str tmp3 = str_init("username");
400         static str tmp4 = str_init("domain");
401         static str tmp5 = str_init("doc");
402
403         if(pxml_force_active) {
404                 *rules_doc = NULL;
405                 return 0;
406         }
407         LM_DBG("[user]= %.*s\t[domain]= %.*s", user->len, user->s, domain->len,
408                         domain->s);
409
410         /* first search in database */
411         query_cols[n_query_cols] = &tmp1;
412         query_vals[n_query_cols].type = DB1_INT;
413         query_vals[n_query_cols].nul = 0;
414         query_vals[n_query_cols].val.int_val = type;
415         n_query_cols++;
416
417         if(file_uri != NULL) {
418                 query_cols[n_query_cols] = &tmp2;
419                 query_vals[n_query_cols].type = DB1_STR;
420                 query_vals[n_query_cols].nul = 0;
421                 query_vals[n_query_cols].val.str_val = *file_uri;
422                 n_query_cols++;
423         } else if(user != NULL && domain != NULL) {
424                 query_cols[n_query_cols] = &tmp3;
425                 query_vals[n_query_cols].type = DB1_STR;
426                 query_vals[n_query_cols].nul = 0;
427                 query_vals[n_query_cols].val.str_val = *user;
428                 n_query_cols++;
429
430                 query_cols[n_query_cols] = &tmp4;
431                 query_vals[n_query_cols].type = DB1_STR;
432                 query_vals[n_query_cols].nul = 0;
433                 query_vals[n_query_cols].val.str_val = *domain;
434                 n_query_cols++;
435         } else {
436                 LM_ERR("Need to specify file uri _OR_ username and domain\n");
437                 return -1;
438         }
439
440         result_cols[xcap_doc_col = n_result_cols++] = &tmp5;
441
442         if(pxml_dbf.use_table(pxml_db, &pxml_xcap_table) < 0) {
443                 LM_ERR("in use_table-[table]= %.*s\n", pxml_xcap_table.len,
444                                 pxml_xcap_table.s);
445                 return -1;
446         }
447
448         if(pxml_dbf.query(pxml_db, query_cols, 0, query_vals, result_cols,
449                            n_query_cols, 1, 0, &result)
450                         < 0) {
451                 LM_ERR("while querying table xcap for [user]=%.*s\t[domain]= %.*s\n",
452                                 user->len, user->s, domain->len, domain->s);
453                 if(result)
454                         pxml_dbf.free_result(pxml_db, result);
455                 return -1;
456         }
457         if(result == NULL)
458                 return -1;
459
460         if(result->n <= 0) {
461                 LM_DBG("No document found in db table for [user]=%.*s"
462                            "\t[domain]= %.*s\t[doc_type]= %d\n",
463                                 user->len, user->s, domain->len, domain->s, type);
464
465                 if(!pxml_integrated_xcap_server && type != PRES_RULES) {
466                         LM_WARN("Cannot retrieve non pres-rules documents from"
467                                         "external XCAP server\n");
468                 } else if(!pxml_integrated_xcap_server) {
469                         if(http_get_rules_doc(*user, *domain, &body) < 0) {
470                                 LM_ERR("sending http GET request to xcap server\n");
471                                 goto error;
472                         }
473                         if(body.s && body.len)
474                                 goto done;
475                 }
476                 pxml_dbf.free_result(pxml_db, result);
477                 return 0;
478         }
479
480         row = &result->rows[xcap_doc_col];
481         row_vals = ROW_VALUES(row);
482
483         body.s = (char *)row_vals[0].val.string_val;
484         if(body.s == NULL) {
485                 LM_ERR("Xcap doc NULL\n");
486                 goto error;
487         }
488         body.len = strlen(body.s);
489         if(body.len == 0) {
490                 LM_ERR("Xcap doc empty\n");
491                 goto error;
492         }
493         LM_DBG("xcap document:\n%.*s", body.len, body.s);
494
495 done:
496         doc = (str *)pkg_malloc(sizeof(str));
497         if(doc == NULL) {
498                 ERR_MEM(PKG_MEM_STR);
499         }
500         doc->s = (char *)pkg_malloc(body.len * sizeof(char));
501         if(doc->s == NULL) {
502                 pkg_free(doc);
503                 ERR_MEM(PKG_MEM_STR);
504         }
505         memcpy(doc->s, body.s, body.len);
506         doc->len = body.len;
507
508         *rules_doc = doc;
509
510         if(result)
511                 pxml_dbf.free_result(pxml_db, result);
512
513         return 1;
514
515 error:
516         if(result)
517                 pxml_dbf.free_result(pxml_db, result);
518
519         return -1;
520 }
521
522 int http_get_rules_doc(str user, str domain, str *rules_doc)
523 {
524         str uri;
525         xcap_doc_sel_t doc_sel;
526         char *doc = NULL;
527         xcap_serv_t *xs;
528         xcap_get_req_t req;
529
530         memset(&req, 0, sizeof(xcap_get_req_t));
531         if(uandd_to_uri(user, domain, &uri) < 0) {
532                 LM_ERR("constructing uri\n");
533                 goto error;
534         }
535
536         doc_sel.auid.s = "pres-rules";
537         doc_sel.auid.len = strlen("pres-rules");
538         doc_sel.doc_type = PRES_RULES;
539         doc_sel.type = USERS_TYPE;
540         doc_sel.xid = uri;
541         doc_sel.filename.s = "index";
542         doc_sel.filename.len = 5;
543
544         /* need the whole document so the node selector is NULL */
545         /* don't know which is the authoritative server for the user
546          * so send request to all in the list */
547         req.doc_sel = doc_sel;
548
549         xs = xs_list;
550         while(xs) {
551                 req.xcap_root = xs->addr;
552                 req.port = xs->port;
553                 doc = xcap_GetNewDoc(req, user, domain);
554                 if(doc != NULL)
555                         break;
556                 xs = xs->next;
557         }
558
559         rules_doc->s = doc;
560         rules_doc->len = doc ? strlen(doc) : 0;
561
562         return 0;
563
564 error:
565         return -1;
566 }