bdb0d7a229cdce2b120eb99cf072734f0f881e5e
[sip-router] / src / modules / auth / auth_mod.c
1 /*
2  * Digest Authentication Module
3  *
4  * Copyright (C) 2001-2003 FhG Fokus
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
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <time.h>
28 #include "../../core/sr_module.h"
29 #include "../../core/dprint.h"
30 #include "../../core/mem/mem.h"
31 #include "../../core/parser/digest/digest.h"
32 #include "../../core/parser/parse_from.h"
33 #include "../../core/parser/parse_to.h"
34 #include "../../core/parser/parse_uri.h"
35 #include "../../core/data_lump.h"
36 #include "../../core/data_lump_rpl.h"
37 #include "../../core/error.h"
38 #include "../../core/ut.h"
39 #include "../../core/pvapi.h"
40 #include "../../core/lvalue.h"
41 #include "../../core/mod_fix.h"
42 #include "../../core/kemi.h"
43 #include "../../core/rand/kam_rand.h"
44 #include "../../modules/sl/sl.h"
45 #include "auth_mod.h"
46 #include "challenge.h"
47 #include "api.h"
48 #include "nid.h"
49 #include "nc.h"
50 #include "ot_nonce.h"
51 #include "rfc2617.h"
52 #include "rfc2617_sha256.h"
53
54 MODULE_VERSION
55
56 #define RAND_SECRET_LEN 32
57
58
59 /*
60  * Module destroy function prototype
61  */
62 static void destroy(void);
63
64 /*
65  * Module initialization function prototype
66  */
67 static int mod_init(void);
68
69 /*
70  * Remove used credentials from a SIP message header
71  */
72 int w_consume_credentials(struct sip_msg* msg, char* s1, char* s2);
73 /*
74  * Check for credentials with given realm
75  */
76 int w_has_credentials(struct sip_msg* msg, char* s1, char* s2);
77
78 static int pv_proxy_authenticate(struct sip_msg* msg, char* realm,
79                 char *passwd, char *flags);
80 static int pv_www_authenticate(struct sip_msg* msg, char* realm,
81                 char *passwd, char *flags);
82 static int pv_www_authenticate2(struct sip_msg* msg, char* realm,
83                 char *passwd, char *flags, char *method);
84 static int fixup_pv_auth(void **param, int param_no);
85 static int w_pv_auth_check(sip_msg_t *msg, char *realm,
86                 char *passwd, char *flags, char *checks);
87 static int fixup_pv_auth_check(void **param, int param_no);
88
89 static int proxy_challenge(struct sip_msg *msg, char* realm, char *flags);
90 static int www_challenge(struct sip_msg *msg, char* realm, char *flags);
91 static int w_auth_challenge(struct sip_msg *msg, char* realm, char *flags);
92 static int fixup_auth_challenge(void **param, int param_no);
93
94 static int w_auth_get_www_authenticate(sip_msg_t* msg, char* realm,
95                 char *flags, char *dst);
96 static int fixup_auth_get_www_authenticate(void **param, int param_no);
97
98 /*
99  * Module parameter variables
100  */
101 char* sec_param    = 0;     /* If the parameter was not used, the secret phrase will be auto-generated */
102 int   nonce_expire = 300;   /* Nonce lifetime */
103 /*int   auth_extra_checks = 0;  -- in nonce.c */
104 int   protect_contacts = 0; /* Do not include contacts in nonce by default */
105 int force_stateless_reply = 0; /* Always send reply statelessly */
106
107 /*! Prefix to strip from realm */
108 str auth_realm_prefix = {"", 0};
109
110 static int auth_use_domain = 0;
111
112 str secret1;
113 str secret2;
114 char* sec_rand1 = 0;
115 char* sec_rand2 = 0;
116
117 str challenge_attr = STR_STATIC_INIT("$digest_challenge");
118 avp_ident_t challenge_avpid;
119
120 str proxy_challenge_header = STR_STATIC_INIT("Proxy-Authenticate");
121 str www_challenge_header = STR_STATIC_INIT("WWW-Authenticate");
122
123 struct qp auth_qop = {
124         STR_STATIC_INIT("auth"),
125         QOP_AUTH
126 };
127
128 static struct qp auth_qauth = {
129         STR_STATIC_INIT("auth"),
130         QOP_AUTH
131 };
132
133 static struct qp auth_qauthint = {
134         STR_STATIC_INIT("auth-int"),
135         QOP_AUTHINT
136 };
137
138 /* Hash algorithm used for digest authentication, MD5 if empty */
139 str auth_algorithm = {"", 0};
140 int hash_hex_len;
141 int add_authinfo_hdr = 0; /* should an Authentication-Info header be added on 200 OK responses? */
142
143 calc_HA1_t calc_HA1;
144 calc_response_t calc_response;
145
146
147 /*! SL API structure */
148 sl_api_t slb;
149
150 /*
151  * Exported functions
152  */
153 static cmd_export_t cmds[] = {
154         {"consume_credentials",    w_consume_credentials,                0,
155                 0, REQUEST_ROUTE},
156         {"www_challenge",          (cmd_function)www_challenge,          2,
157                 fixup_auth_challenge, REQUEST_ROUTE},
158         {"proxy_challenge",        (cmd_function)proxy_challenge,        2,
159                 fixup_auth_challenge, REQUEST_ROUTE},
160         {"auth_challenge",         (cmd_function)w_auth_challenge,       2,
161                 fixup_auth_challenge, REQUEST_ROUTE},
162         {"pv_www_authorize",       (cmd_function)pv_www_authenticate,    3,
163                 fixup_pv_auth, REQUEST_ROUTE},
164         {"pv_www_authenticate",    (cmd_function)pv_www_authenticate,    3,
165                 fixup_pv_auth, REQUEST_ROUTE},
166         {"pv_www_authenticate",    (cmd_function)pv_www_authenticate2,   4,
167                 fixup_pv_auth, REQUEST_ROUTE},
168         {"pv_proxy_authorize",     (cmd_function)pv_proxy_authenticate,  3,
169                 fixup_pv_auth, REQUEST_ROUTE},
170         {"pv_proxy_authenticate",  (cmd_function)pv_proxy_authenticate,  3,
171                 fixup_pv_auth, REQUEST_ROUTE},
172         {"auth_get_www_authenticate",  (cmd_function)w_auth_get_www_authenticate,  3,
173                 fixup_auth_get_www_authenticate, REQUEST_ROUTE},
174         {"has_credentials",        w_has_credentials,                    1,
175                 fixup_spve_null, REQUEST_ROUTE},
176         {"pv_auth_check",         (cmd_function)w_pv_auth_check,           4,
177                 fixup_pv_auth_check, REQUEST_ROUTE},
178         {"bind_auth_s",           (cmd_function)bind_auth_s, 0, 0, 0        },
179         {0, 0, 0, 0, 0}
180 };
181
182
183 /*
184  * Exported parameters
185  */
186 static param_export_t params[] = {
187         {"secret",                 PARAM_STRING, &sec_param             },
188         {"nonce_expire",           PARAM_INT,    &nonce_expire          },
189         {"nonce_auth_max_drift",   PARAM_INT,    &nonce_auth_max_drift  },
190         {"protect_contacts",       PARAM_INT,    &protect_contacts      },
191         {"challenge_attr",         PARAM_STR,    &challenge_attr        },
192         {"proxy_challenge_header", PARAM_STR,    &proxy_challenge_header},
193         {"www_challenge_header",   PARAM_STR,    &www_challenge_header  },
194         {"qop",                    PARAM_STR,    &auth_qop.qop_str      },
195         {"auth_checks_register",   PARAM_INT,    &auth_checks_reg       },
196         {"auth_checks_no_dlg",     PARAM_INT,    &auth_checks_ood       },
197         {"auth_checks_in_dlg",     PARAM_INT,    &auth_checks_ind       },
198         {"nonce_count"  ,          PARAM_INT,    &nc_enabled            },
199         {"nc_array_size",          PARAM_INT,    &nc_array_size         },
200         {"nc_array_order",         PARAM_INT,    &nc_array_k            },
201         {"one_time_nonce"  ,       PARAM_INT,    &otn_enabled           },
202         {"otn_in_flight_no",       PARAM_INT,    &otn_in_flight_no      },
203         {"otn_in_flight_order",    PARAM_INT,    &otn_in_flight_k       },
204         {"nid_pool_no",            PARAM_INT,    &nid_pool_no           },
205         {"force_stateless_reply",  PARAM_INT,    &force_stateless_reply },
206         {"realm_prefix",           PARAM_STRING, &auth_realm_prefix.s   },
207         {"use_domain",             PARAM_INT,    &auth_use_domain       },
208         {"algorithm",              PARAM_STR,    &auth_algorithm        },
209         {"add_authinfo_hdr",       INT_PARAM,    &add_authinfo_hdr      },
210         {0, 0, 0}
211 };
212
213
214 /*
215  * Module interface
216  */
217 struct module_exports exports = {
218         "auth",
219         cmds,
220         0,          /* RPC methods */
221         params,
222         mod_init,   /* module initialization function */
223         0,          /* response function */
224         destroy,    /* destroy function */
225         0,          /* oncancel function */
226         0           /* child initialization function */
227 };
228
229
230 /*
231  * Secret parameter was not used so we generate
232  * a random value here
233  */
234 static inline int generate_random_secret(void)
235 {
236         int i;
237
238         sec_rand1 = (char*)pkg_malloc(RAND_SECRET_LEN);
239         sec_rand2 = (char*)pkg_malloc(RAND_SECRET_LEN);
240         if (!sec_rand1 || !sec_rand2) {
241                 LM_ERR("No memory left\n");
242                 if (sec_rand1){
243                         pkg_free(sec_rand1);
244                         sec_rand1=0;
245                 }
246                 return -1;
247         }
248
249         /* srandom(time(0));  -- seeded by core */
250
251         for(i = 0; i < RAND_SECRET_LEN; i++) {
252                 sec_rand1[i] = 32 + (int)(95.0 * kam_rand() / (KAM_RAND_MAX + 1.0));
253         }
254
255         secret1.s = sec_rand1;
256         secret1.len = RAND_SECRET_LEN;
257
258         for(i = 0; i < RAND_SECRET_LEN; i++) {
259                 sec_rand2[i] = 32 + (int)(95.0 * kam_rand() / (KAM_RAND_MAX + 1.0));
260         }
261
262         secret2.s = sec_rand2;
263         secret2.len = RAND_SECRET_LEN;
264
265         /* DBG("Generated secret: '%.*s'\n", secret.len, secret.s); */
266
267         return 0;
268 }
269
270
271 static int mod_init(void)
272 {
273         str attr;
274
275         DBG("auth module - initializing\n");
276
277         auth_realm_prefix.len = strlen(auth_realm_prefix.s);
278
279         /* bind the SL API */
280         if (sl_load_api(&slb)!=0) {
281                 LM_ERR("cannot bind to SL API\n");
282                 return -1;
283         }
284
285         /* If the parameter was not used */
286         if (sec_param == 0) {
287                 /* Generate secret using random generator */
288                 if (generate_random_secret() < 0) {
289                         LM_ERR("Error while generating random secret\n");
290                         return -3;
291                 }
292         } else {
293                 /* Otherwise use the parameter's value */
294                 secret1.s = sec_param;
295                 secret1.len = strlen(secret1.s);
296
297                 if (auth_checks_reg || auth_checks_ind || auth_checks_ood) {
298                         /* divide the secret in half: one half for secret1 and one half for
299                          *  secret2 */
300                         secret2.len = secret1.len/2;
301                         secret1.len -= secret2.len;
302                         secret2.s = secret1.s + secret1.len;
303                         if (secret2.len < 16) {
304                                 LM_WARN("consider a longer secret when extra auth checks are"
305                                                 " enabled (the config secret is divided in 2!)\n");
306                         }
307                 }
308         }
309
310         if ((!challenge_attr.s || challenge_attr.len == 0) ||
311                         challenge_attr.s[0] != '$') {
312                 LM_ERR("Invalid value of challenge_attr module parameter\n");
313                 return -1;
314         }
315
316         attr.s = challenge_attr.s + 1;
317         attr.len = challenge_attr.len - 1;
318
319         if (parse_avp_ident(&attr, &challenge_avpid) < 0) {
320                 LM_ERR("Error while parsing value of challenge_attr module"
321                                 " parameter\n");
322                 return -1;
323         }
324
325         parse_qop(&auth_qop);
326         switch(auth_qop.qop_parsed){
327                 case QOP_OTHER:
328                         LM_ERR("Unsupported qop parameter value\n");
329                         return -1;
330                 case QOP_AUTH:
331                 case QOP_AUTHINT:
332                         if (nc_enabled){
333 #ifndef USE_NC
334                                 LM_WARN("nounce count support enabled from config, but"
335                                                 " disabled at compile time (recompile with -DUSE_NC)\n");
336                                 nc_enabled=0;
337 #else
338                                 if (nid_crt==0)
339                                         init_nonce_id();
340                                 if (init_nonce_count()!=0)
341                                         return -1;
342 #endif
343                         }
344 #ifdef USE_NC
345                         else{
346                                 LM_INFO("qop set, but nonce-count (nc_enabled) support"
347                                                 " disabled\n");
348                         }
349 #endif
350                         break;
351                 default:
352                         if (nc_enabled){
353                                 LM_WARN("nonce-count support enabled, but qop not set\n");
354                                 nc_enabled=0;
355                         }
356                         break;
357         }
358         if (otn_enabled){
359 #ifdef USE_OT_NONCE
360                 if (nid_crt==0) init_nonce_id();
361                 if (init_ot_nonce()!=0)
362                         return -1;
363 #else
364                 LM_WARN("one-time-nonce support enabled from config, but "
365                                 "disabled at compile time (recompile with -DUSE_OT_NONCE)\n");
366                 otn_enabled=0;
367 #endif /* USE_OT_NONCE */
368         }
369
370         if (auth_algorithm.len == 0 || strcmp(auth_algorithm.s, "MD5") == 0) {
371                 hash_hex_len = HASHHEXLEN;
372                 calc_HA1 = calc_HA1_md5;
373                 calc_response = calc_response_md5;
374         }
375         else if (strcmp(auth_algorithm.s, "SHA-256") == 0) {
376                 hash_hex_len = HASHHEXLEN_SHA256;
377                 calc_HA1 = calc_HA1_sha256;
378                 calc_response = calc_response_sha256;
379         }
380         else {
381                 LM_ERR("Invalid algorithm provided."
382                                 " Possible values are \"\", \"MD5\" or \"SHA-256\"\n");
383                 return -1;
384         }
385
386         return 0;
387 }
388
389
390 static void destroy(void)
391 {
392         if (sec_rand1) pkg_free(sec_rand1);
393         if (sec_rand2) pkg_free(sec_rand2);
394 #ifdef USE_NC
395         destroy_nonce_count();
396 #endif
397 #ifdef USE_OT_NONCE
398         destroy_ot_nonce();
399 #endif
400 #if defined USE_NC || defined USE_OT_NONCE
401         destroy_nonce_id();
402 #endif
403 }
404
405
406 /*
407  * Remove used credentials from a SIP message header
408  */
409 int consume_credentials(struct sip_msg* msg)
410 {
411         struct hdr_field* h;
412         int len;
413
414         /* skip requests that can't be authenticated */
415         if (msg->REQ_METHOD & (METHOD_ACK|METHOD_CANCEL|METHOD_PRACK))
416                 return -1;
417         get_authorized_cred(msg->authorization, &h);
418         if (!h) {
419                 get_authorized_cred(msg->proxy_auth, &h);
420                 if (!h) {
421                         LM_ERR("No authorized credentials found (error in scripts)\n");
422                         return -1;
423                 }
424         }
425
426         len = h->len;
427
428         if (del_lump(msg, h->name.s - msg->buf, len, 0) == 0) {
429                 LM_ERR("Can't remove credentials\n");
430                 return -1;
431         }
432
433         return 1;
434 }
435
436 /**
437  *
438  */
439 int w_consume_credentials(struct sip_msg* msg, char* s1, char* s2)
440 {
441         return consume_credentials(msg);
442 }
443
444 /**
445  *
446  */
447 int ki_has_credentials(sip_msg_t *msg, str* srealm)
448 {
449         hdr_field_t *hdr = NULL;
450         int ret;
451
452         ret = find_credentials(msg, srealm, HDR_PROXYAUTH_T, &hdr);
453         if(ret==0) {
454                 LM_DBG("found www credentials with realm [%.*s]\n", srealm->len, srealm->s);
455                 return 1;
456         }
457         ret = find_credentials(msg, srealm, HDR_AUTHORIZATION_T, &hdr);
458         if(ret==0) {
459                 LM_DBG("found proxy credentials with realm [%.*s]\n", srealm->len, srealm->s);
460                 return 1;
461         }
462
463         LM_DBG("no credentials with realm [%.*s]\n", srealm->len, srealm->s);
464         return -1;
465 }
466
467 /**
468  *
469  */
470 int w_has_credentials(sip_msg_t *msg, char* realm, char* s2)
471 {
472         str srealm  = {0, 0};
473
474         if (fixup_get_svalue(msg, (gparam_t*)realm, &srealm) < 0) {
475                 LM_ERR("failed to get realm value\n");
476                 return -1;
477         }
478         return ki_has_credentials(msg, &srealm);
479 }
480 /**
481  * @brief do WWW-Digest authentication with password taken from cfg var
482  */
483 int pv_authenticate(struct sip_msg *msg, str *realm, str *passwd,
484                 int flags, int hftype, str *method)
485 {
486         struct hdr_field* h;
487         auth_body_t* cred;
488         int ret;
489         str hf = {0, 0};
490         avp_value_t val;
491         static char ha1[256];
492         struct qp *qop = NULL;
493
494         cred = 0;
495         ret = AUTH_ERROR;
496
497         switch(pre_auth(msg, realm, hftype, &h, NULL)) {
498                 case NONCE_REUSED:
499                         LM_DBG("nonce reused");
500                         ret = AUTH_NONCE_REUSED;
501                         goto end;
502                 case STALE_NONCE:
503                         LM_DBG("stale nonce\n");
504                         ret = AUTH_STALE_NONCE;
505                         goto end;
506                 case NO_CREDENTIALS:
507                         LM_DBG("no credentials\n");
508                         ret = AUTH_NO_CREDENTIALS;
509                         goto end;
510                 case ERROR:
511                 case BAD_CREDENTIALS:
512                         LM_DBG("error or bad credentials\n");
513                         ret = AUTH_ERROR;
514                         goto end;
515                 case CREATE_CHALLENGE:
516                         LM_ERR("CREATE_CHALLENGE is not a valid state\n");
517                         ret = AUTH_ERROR;
518                         goto end;
519                 case DO_RESYNCHRONIZATION:
520                         LM_ERR("DO_RESYNCHRONIZATION is not a valid state\n");
521                         ret = AUTH_ERROR;
522                         goto end;
523                 case NOT_AUTHENTICATED:
524                         LM_DBG("not authenticated\n");
525                         ret = AUTH_ERROR;
526                         goto end;
527                 case DO_AUTHENTICATION:
528                         break;
529                 case AUTHENTICATED:
530                         ret = AUTH_OK;
531                         goto end;
532         }
533
534         cred = (auth_body_t*)h->parsed;
535
536         /* compute HA1 if needed */
537         if ((flags&1)==0) {
538                 /* Plaintext password is stored in PV, calculate HA1 */
539                 calc_HA1(HA_MD5, &cred->digest.username.whole, realm,
540                                 passwd, 0, 0, ha1);
541                 LM_DBG("HA1 string calculated: %s\n", ha1);
542         } else {
543                 memcpy(ha1, passwd->s, passwd->len);
544                 ha1[passwd->len] = '\0';
545         }
546
547         /* Recalculate response, it must be same to authorize successfully */
548         ret = auth_check_response(&(cred->digest), method, ha1);
549         if(ret==AUTHENTICATED) {
550                 ret = AUTH_OK;
551                 switch(post_auth(msg, h, ha1)) {
552                         case AUTHENTICATED:
553                                 break;
554                         default:
555                                 ret = AUTH_ERROR;
556                                 break;
557                 }
558         } else {
559                 if(ret==NOT_AUTHENTICATED)
560                         ret = AUTH_INVALID_PASSWORD;
561                 else
562                         ret = AUTH_ERROR;
563         }
564
565 end:
566         if (ret < 0) {
567                 /* check if required to add challenge header as avp */
568                 if(!(flags&14))
569                         return ret;
570                 if(flags&8) {
571                         qop = &auth_qauthint;
572                 } else if(flags&4) {
573                         qop = &auth_qauth;
574                 }
575                 if (get_challenge_hf(msg, (cred ? cred->stale : 0),
576                                         realm, NULL, (auth_algorithm.len ? &auth_algorithm : NULL), qop, hftype, &hf) < 0) {
577                         LM_ERR("Error while creating challenge\n");
578                         ret = AUTH_ERROR;
579                 } else {
580                         val.s = hf;
581                         if(add_avp(challenge_avpid.flags | AVP_VAL_STR,
582                                                 challenge_avpid.name, val) < 0) {
583                                 LM_ERR("Error while creating attribute with challenge\n");
584                                 ret = AUTH_ERROR;
585                         }
586                         pkg_free(hf.s);
587                 }
588         }
589
590         return ret;
591 }
592
593 /**
594  *
595  */
596 static int pv_proxy_authenticate(struct sip_msg *msg, char* realm,
597                 char *passwd, char *flags)
598 {
599         int vflags = 0;
600         str srealm  = {0, 0};
601         str spasswd = {0, 0};
602
603         if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
604                 LM_ERR("failed to get realm value\n");
605                 goto error;
606         }
607
608         if(srealm.len==0) {
609                 LM_ERR("invalid realm value - empty content\n");
610                 goto error;
611         }
612
613         if (get_str_fparam(&spasswd, msg, (fparam_t*)passwd) < 0) {
614                 LM_ERR("failed to get passwd value\n");
615                 goto error;
616         }
617
618         if(spasswd.len==0) {
619                 LM_ERR("invalid password value - empty content\n");
620                 goto error;
621         }
622
623         if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
624                 LM_ERR("invalid flags value\n");
625                 goto error;
626         }
627         return pv_authenticate(msg, &srealm, &spasswd, vflags, HDR_PROXYAUTH_T,
628                         &msg->first_line.u.request.method);
629
630 error:
631         return AUTH_ERROR;
632 }
633
634 /**
635  *
636  */
637 static int pv_www_authenticate(struct sip_msg *msg, char* realm,
638                 char *passwd, char *flags)
639 {
640         int vflags = 0;
641         str srealm  = {0, 0};
642         str spasswd = {0, 0};
643
644         if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
645                 LM_ERR("failed to get realm value\n");
646                 goto error;
647         }
648
649         if(srealm.len==0) {
650                 LM_ERR("invalid realm value - empty content\n");
651                 goto error;
652         }
653
654         if (get_str_fparam(&spasswd, msg, (fparam_t*)passwd) < 0) {
655                 LM_ERR("failed to get passwd value\n");
656                 goto error;
657         }
658
659         if(spasswd.len==0) {
660                 LM_ERR("invalid password value - empty content\n");
661                 goto error;
662         }
663
664         if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
665                 LM_ERR("invalid flags value\n");
666                 goto error;
667         }
668         return pv_authenticate(msg, &srealm, &spasswd, vflags, HDR_AUTHORIZATION_T,
669                         &msg->first_line.u.request.method);
670
671 error:
672         return AUTH_ERROR;
673 }
674
675 static int pv_www_authenticate2(struct sip_msg *msg, char* realm,
676                 char *passwd, char *flags, char *method)
677 {
678         int vflags = 0;
679         str srealm  = {0, 0};
680         str spasswd = {0, 0};
681         str smethod = {0, 0};
682
683         if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
684                 LM_ERR("failed to get realm value\n");
685                 goto error;
686         }
687
688         if(srealm.len==0) {
689                 LM_ERR("invalid realm value - empty content\n");
690                 goto error;
691         }
692
693         if (get_str_fparam(&spasswd, msg, (fparam_t*)passwd) < 0) {
694                 LM_ERR("failed to get passwd value\n");
695                 goto error;
696         }
697
698         if(spasswd.len==0) {
699                 LM_ERR("invalid password value - empty content\n");
700                 goto error;
701         }
702
703         if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
704                 LM_ERR("invalid flags value\n");
705                 goto error;
706         }
707
708         if (get_str_fparam(&smethod, msg, (fparam_t*)method) < 0) {
709                 LM_ERR("failed to get method value from msg %p var %p\n", msg, method);
710                 goto error;
711         }
712
713         if(smethod.len==0) {
714                 LM_ERR("invalid method value - empty content\n");
715                 goto error;
716         }
717
718         return pv_authenticate(msg, &srealm, &spasswd, vflags, HDR_AUTHORIZATION_T,
719                         &smethod);
720
721 error:
722         return AUTH_ERROR;
723 }
724
725 /**
726  *
727  */
728 static int pv_auth_check(sip_msg_t *msg, str *srealm, str *spasswd, int vflags,
729                 int vchecks)
730 {
731         int ret;
732         hdr_field_t *hdr;
733         sip_uri_t *uri = NULL;
734         sip_uri_t *turi = NULL;
735         sip_uri_t *furi = NULL;
736         str suser;
737
738         if(msg->REQ_METHOD==METHOD_REGISTER)
739                 ret = pv_authenticate(msg, srealm, spasswd, vflags, HDR_AUTHORIZATION_T,
740                                 &msg->first_line.u.request.method);
741         else
742                 ret = pv_authenticate(msg, srealm, spasswd, vflags, HDR_PROXYAUTH_T,
743                                 &msg->first_line.u.request.method);
744
745         if(ret==AUTH_OK && (vchecks&AUTH_CHECK_ID_F)) {
746                 hdr = (msg->proxy_auth==0)?msg->authorization:msg->proxy_auth;
747                 suser = ((auth_body_t*)(hdr->parsed))->digest.username.user;
748
749                 if((furi=parse_from_uri(msg))==NULL)
750                         return AUTH_ERROR;
751
752                 if(msg->REQ_METHOD==METHOD_REGISTER || msg->REQ_METHOD==METHOD_PUBLISH) {
753                         if((turi=parse_to_uri(msg))==NULL)
754                                 return AUTH_ERROR;
755                         uri = turi;
756                 } else {
757                         uri = furi;
758                 }
759                 if(suser.len!=uri->user.len
760                                 || strncmp(suser.s, uri->user.s, suser.len)!=0)
761                         return AUTH_USER_MISMATCH;
762
763                 if(msg->REQ_METHOD==METHOD_REGISTER || msg->REQ_METHOD==METHOD_PUBLISH) {
764                         /* check from==to */
765                         if(furi->user.len!=turi->user.len
766                                         || strncmp(furi->user.s, turi->user.s, furi->user.len)!=0)
767                                 return AUTH_USER_MISMATCH;
768                         if(auth_use_domain!=0 && (furi->host.len!=turi->host.len
769                                                 || strncmp(furi->host.s, turi->host.s, furi->host.len)!=0))
770                                 return AUTH_USER_MISMATCH;
771                         /* check r-uri==from for publish */
772                         if(msg->REQ_METHOD==METHOD_PUBLISH) {
773                                 if(parse_sip_msg_uri(msg)<0)
774                                         return AUTH_ERROR;
775                                 uri = &msg->parsed_uri;
776                                 if(furi->user.len!=uri->user.len
777                                                 || strncmp(furi->user.s, uri->user.s, furi->user.len)!=0)
778                                         return AUTH_USER_MISMATCH;
779                                 if(auth_use_domain!=0 && (furi->host.len!=uri->host.len
780                                                         || strncmp(furi->host.s, uri->host.s, furi->host.len)!=0))
781                                         return AUTH_USER_MISMATCH;
782                         }
783                 }
784                 return AUTH_OK;
785         }
786
787         return ret;
788 }
789
790 /**
791  *
792  */
793 static int w_pv_auth_check(sip_msg_t *msg, char *realm,
794                 char *passwd, char *flags, char *checks)
795 {
796         int vflags = 0;
797         int vchecks = 0;
798         str srealm  = {0, 0};
799         str spasswd = {0, 0};
800
801
802         if(msg==NULL) {
803                 LM_ERR("invalid msg parameter\n");
804                 return AUTH_ERROR;
805         }
806
807         if ((msg->REQ_METHOD == METHOD_ACK) || (msg->REQ_METHOD == METHOD_CANCEL)) {
808                 return AUTH_OK;
809         }
810
811         if(realm==NULL || passwd==NULL || flags==NULL || checks==NULL) {
812                 LM_ERR("invalid parameters\n");
813                 return AUTH_ERROR;
814         }
815
816         if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
817                 LM_ERR("failed to get realm value\n");
818                 return AUTH_ERROR;
819         }
820
821         if(srealm.len==0) {
822                 LM_ERR("invalid realm value - empty content\n");
823                 return AUTH_ERROR;
824         }
825
826         if (get_str_fparam(&spasswd, msg, (fparam_t*)passwd) < 0) {
827                 LM_ERR("failed to get passwd value\n");
828                 return AUTH_ERROR;
829         }
830
831         if(spasswd.len==0) {
832                 LM_ERR("invalid password value - empty content\n");
833                 return AUTH_ERROR;
834         }
835
836         if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
837                 LM_ERR("invalid flags value\n");
838                 return AUTH_ERROR;
839         }
840
841         if (get_int_fparam(&vchecks, msg, (fparam_t*)checks) < 0) {
842                 LM_ERR("invalid checks value\n");
843                 return AUTH_ERROR;
844         }
845         LM_DBG("realm [%.*s] flags [%d] checks [%d]\n", srealm.len, srealm.s,
846                         vflags, vchecks);
847         return pv_auth_check(msg, &srealm, &spasswd, vflags, vchecks);
848 }
849
850
851 /**
852  * @brief fixup function for pv_{www,proxy}_authenticate
853  */
854 static int fixup_pv_auth(void **param, int param_no)
855 {
856         if(strlen((char*)*param)<=0) {
857                 LM_ERR("empty parameter %d not allowed\n", param_no);
858                 return -1;
859         }
860
861         switch(param_no) {
862                 case 1:
863                 case 2:
864                 case 4:
865                         return fixup_var_pve_str_12(param, 1);
866                 case 3:
867                         return fixup_var_int_12(param, 1);
868         }
869         return 0;
870 }
871
872 /**
873  * @brief fixup function for pv_{www,proxy}_authenticate
874  */
875 static int fixup_pv_auth_check(void **param, int param_no)
876 {
877         if(strlen((char*)*param)<=0) {
878                 LM_ERR("empty parameter %d not allowed\n", param_no);
879                 return -1;
880         }
881
882         switch(param_no) {
883                 case 1:
884                 case 2:
885                         return fixup_var_pve_str_12(param, 1);
886                 case 3:
887                 case 4:
888                         return fixup_var_int_12(param, 1);
889         }
890         return 0;
891 }
892
893
894
895 /**
896  *
897  */
898 static int auth_send_reply(struct sip_msg *msg, int code, char *reason,
899                 char *hdr, int hdr_len)
900 {
901         str reason_str;
902
903         /* Add new headers if there are any */
904         if ((hdr!=NULL) && (hdr_len>0)) {
905                 if (add_lump_rpl(msg, hdr, hdr_len, LUMP_RPL_HDR)==0) {
906                         LM_ERR("failed to append hdr to reply\n");
907                         return -1;
908                 }
909         }
910
911         reason_str.s = reason;
912         reason_str.len = strlen(reason);
913
914         return force_stateless_reply ?
915                 slb.sreply(msg, code, &reason_str) :
916                 slb.freply(msg, code, &reason_str);
917 }
918
919 /**
920  *
921  */
922 int auth_challenge_helper(struct sip_msg *msg, str *realm, int flags, int hftype,
923                 str *res)
924 {
925         int ret, stale;
926         str hf = {0, 0};
927         struct qp *qop = NULL;
928
929         ret = -1;
930
931         if(flags&2) {
932                 qop = &auth_qauthint;
933         } else if(flags&1) {
934                 qop = &auth_qauth;
935         }
936         if (flags & 16) {
937                 stale = 1;
938         } else {
939                 stale = 0;
940         }
941         if (get_challenge_hf(msg, stale, realm, NULL, (auth_algorithm.len ? &auth_algorithm : NULL), qop, hftype, &hf)
942                         < 0) {
943                 LM_ERR("Error while creating challenge\n");
944                 ret = -2;
945                 goto error;
946         }
947
948         ret = 1;
949         if(res!=NULL)
950         {
951                 *res = hf;
952                 return ret;
953         }
954         switch(hftype) {
955                 case HDR_AUTHORIZATION_T:
956                         if(auth_send_reply(msg, 401, "Unauthorized",
957                                                 hf.s, hf.len) <0 )
958                                 ret = -3;
959                         break;
960                 case HDR_PROXYAUTH_T:
961                         if(auth_send_reply(msg, 407, "Proxy Authentication Required",
962                                                 hf.s, hf.len) <0 )
963                                 ret = -3;
964                         break;
965         }
966         if(hf.s) pkg_free(hf.s);
967         return ret;
968
969 error:
970         if(hf.s) pkg_free(hf.s);
971         if(!(flags&4)) {
972                 if(auth_send_reply(msg, 500, "Internal Server Error", 0, 0) <0 )
973                         ret = -4;
974         }
975         return ret;
976 }
977
978 /**
979  *
980  */
981 int auth_challenge_hftype(struct sip_msg *msg, str *realm, int flags, int hftype)
982 {
983         return auth_challenge_helper(msg, realm, flags, hftype, NULL);
984 }
985
986 /**
987  *
988  */
989 int auth_challenge(sip_msg_t *msg, str *realm, int flags)
990 {
991         int htype;
992
993         if(msg==NULL) return -1;
994
995         if(msg->REQ_METHOD==METHOD_REGISTER)
996                 htype = HDR_AUTHORIZATION_T;
997         else
998                 htype = HDR_PROXYAUTH_T;
999
1000         return auth_challenge_helper(msg, realm, flags, htype, NULL);
1001 }
1002
1003 /**
1004  *
1005  */
1006 static int proxy_challenge(struct sip_msg *msg, char* realm, char *flags)
1007 {
1008         int vflags = 0;
1009         str srealm  = {0, 0};
1010
1011         if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
1012                 LM_ERR("failed to get realm value\n");
1013                 goto error;
1014         }
1015
1016         if(srealm.len==0) {
1017                 LM_ERR("invalid realm value - empty content\n");
1018                 goto error;
1019         }
1020
1021         if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
1022                 LM_ERR("invalid flags value\n");
1023                 goto error;
1024         }
1025
1026         return auth_challenge_hftype(msg, &srealm, vflags, HDR_PROXYAUTH_T);
1027
1028 error:
1029         if(!(vflags&4)) {
1030                 if(auth_send_reply(msg, 500, "Internal Server Error", 0, 0) <0 )
1031                         return -4;
1032         }
1033         return -1;
1034 }
1035
1036 /**
1037  *
1038  */
1039 static int www_challenge(struct sip_msg *msg, char* realm, char *flags)
1040 {
1041         int vflags = 0;
1042         str srealm  = {0, 0};
1043
1044         if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
1045                 LM_ERR("failed to get realm value\n");
1046                 goto error;
1047         }
1048
1049         if(srealm.len==0) {
1050                 LM_ERR("invalid realm value - empty content\n");
1051                 goto error;
1052         }
1053
1054         if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
1055                 LM_ERR("invalid flags value\n");
1056                 goto error;
1057         }
1058
1059         return auth_challenge_hftype(msg, &srealm, vflags, HDR_AUTHORIZATION_T);
1060
1061 error:
1062         if(!(vflags&4)) {
1063                 if(auth_send_reply(msg, 500, "Internal Server Error", 0, 0) <0 )
1064                         return -4;
1065         }
1066         return -1;
1067 }
1068
1069 /**
1070  *
1071  */
1072 static int w_auth_challenge(struct sip_msg *msg, char* realm, char *flags)
1073 {
1074         int vflags = 0;
1075         str srealm  = {0, 0};
1076
1077         if((msg->REQ_METHOD == METHOD_ACK) || (msg->REQ_METHOD == METHOD_CANCEL)) {
1078                 return 1;
1079         }
1080
1081         if(get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
1082                 LM_ERR("failed to get realm value\n");
1083                 goto error;
1084         }
1085
1086         if(srealm.len==0) {
1087                 LM_ERR("invalid realm value - empty content\n");
1088                 goto error;
1089         }
1090
1091         if(get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
1092                 LM_ERR("invalid flags value\n");
1093                 goto error;
1094         }
1095
1096         if(msg->REQ_METHOD==METHOD_REGISTER)
1097                 return auth_challenge_hftype(msg, &srealm, vflags, HDR_AUTHORIZATION_T);
1098         else
1099                 return auth_challenge_hftype(msg, &srealm, vflags, HDR_PROXYAUTH_T);
1100
1101 error:
1102         if(!(vflags&4)) {
1103                 if(auth_send_reply(msg, 500, "Internal Server Error", 0, 0) <0 )
1104                         return -4;
1105         }
1106         return -1;
1107 }
1108
1109
1110 /**
1111  * @brief fixup function for {www,proxy}_challenge
1112  */
1113 static int fixup_auth_challenge(void **param, int param_no)
1114 {
1115         if(strlen((char*)*param)<=0) {
1116                 LM_ERR("empty parameter %d not allowed\n", param_no);
1117                 return -1;
1118         }
1119
1120         switch(param_no) {
1121                 case 1:
1122                         return fixup_var_str_12(param, 1);
1123                 case 2:
1124                         return fixup_var_int_12(param, 1);
1125         }
1126         return 0;
1127 }
1128
1129
1130 /**
1131  *
1132  */
1133 static int w_auth_get_www_authenticate(sip_msg_t* msg, char* realm,
1134                 char *flags, char *dst)
1135 {
1136         int vflags = 0;
1137         str srealm  = {0};
1138         str hf = {0};
1139         pv_spec_t *pv;
1140         pv_value_t val;
1141         int ret;
1142
1143         if(get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
1144                 LM_ERR("failed to get realm value\n");
1145                 goto error;
1146         }
1147
1148         if(srealm.len==0) {
1149                 LM_ERR("invalid realm value - empty content\n");
1150                 goto error;
1151         }
1152
1153         if(get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
1154                 LM_ERR("invalid flags value\n");
1155                 goto error;
1156         }
1157
1158         pv = (pv_spec_t *)dst;
1159
1160         ret = auth_challenge_helper(NULL, &srealm, vflags,
1161                         HDR_AUTHORIZATION_T, &hf);
1162
1163         if(ret<0)
1164                 return ret;
1165
1166         val.rs.s = pv_get_buffer();
1167         val.rs.len = 0;
1168         if(hf.s!=NULL)
1169         {
1170                 memcpy(val.rs.s, hf.s, hf.len);
1171                 val.rs.len = hf.len;
1172                 val.rs.s[val.rs.len] = '\0';
1173                 pkg_free(hf.s);
1174         }
1175         val.flags = PV_VAL_STR;
1176         pv->setf(msg, &pv->pvp, (int)EQ_T, &val);
1177
1178         return ret;
1179
1180 error:
1181         return -1;
1182 }
1183
1184
1185 static int fixup_auth_get_www_authenticate(void **param, int param_no)
1186 {
1187         if(strlen((char*)*param)<=0) {
1188                 LM_ERR("empty parameter %d not allowed\n", param_no);
1189                 return -1;
1190         }
1191
1192         switch(param_no) {
1193                 case 1:
1194                         return fixup_var_str_12(param, 1);
1195                 case 2:
1196                         return fixup_var_int_12(param, 1);
1197                 case 3:
1198                         if (fixup_pvar_null(param, 1) != 0) {
1199                                 LM_ERR("failed to fixup result pvar\n");
1200                                 return -1;
1201                         }
1202                         if (((pv_spec_t *)(*param))->setf == NULL) {
1203                                 LM_ERR("result pvar is not writeble\n");
1204                                 return -1;
1205                         }
1206                         return 0;
1207         }
1208         return 0;
1209 }
1210
1211 /**
1212  *
1213  */
1214 static sr_kemi_t sr_kemi_auth_exports[] = {
1215         { str_init("auth"), str_init("consume_credentials"),
1216                 SR_KEMIP_INT, consume_credentials,
1217                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1218                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1219         },
1220         { str_init("auth"), str_init("auth_challenge"),
1221                 SR_KEMIP_INT, auth_challenge,
1222                 { SR_KEMIP_STR, SR_KEMIP_INT, SR_KEMIP_NONE,
1223                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1224         },
1225         { str_init("auth"), str_init("pv_auth_check"),
1226                 SR_KEMIP_INT, pv_auth_check,
1227                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_INT,
1228                         SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE }
1229         },
1230         { str_init("auth"), str_init("has_credentials"),
1231                 SR_KEMIP_INT, ki_has_credentials,
1232                 { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
1233                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1234         },
1235
1236         { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
1237 };
1238
1239 /**
1240  *
1241  */
1242 int mod_register(char *path, int *dlflags, void *p1, void *p2)
1243 {
1244         sr_kemi_modules_add(sr_kemi_auth_exports);
1245         return 0;
1246 }