src/modules/auth/auth_mod.c
6f43dd16
 /*
9f0c8882
  * Digest Authentication Module
c4b6428c
  *
95072403
  * Copyright (C) 2001-2003 FhG Fokus
c4b6428c
  *
4ca850ad
  * This file is part of Kamailio, a free SIP server.
c4b6428c
  *
4ca850ad
  * Kamailio is free software; you can redistribute it and/or modify
ed346bcc
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
c4b6428c
  *
4ca850ad
  * Kamailio is distributed in the hope that it will be useful,
ed346bcc
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
c4b6428c
  *
  * You should have received a copy of the GNU General Public License
6f43dd16
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
b6b30d77
  *
2d429fbd
  */
 
7ae39a9b
 
59f8bf50
 #include <stdio.h>
2d429fbd
 #include <stdlib.h>
c60d97dd
 #include <time.h>
cf83221d
 #include "../../core/sr_module.h"
 #include "../../core/dprint.h"
 #include "../../core/mem/mem.h"
 #include "../../core/parser/digest/digest.h"
 #include "../../core/parser/parse_from.h"
 #include "../../core/parser/parse_to.h"
 #include "../../core/parser/parse_uri.h"
 #include "../../core/data_lump.h"
 #include "../../core/data_lump_rpl.h"
 #include "../../core/error.h"
 #include "../../core/ut.h"
 #include "../../core/pvapi.h"
 #include "../../core/lvalue.h"
 #include "../../core/mod_fix.h"
 #include "../../core/kemi.h"
 #include "../../core/rand/kam_rand.h"
39f66d80
 #include "../../modules/sl/sl.h"
eef11a9f
 #include "auth_mod.h"
9f0c8882
 #include "challenge.h"
c60d97dd
 #include "api.h"
12690ddb
 #include "nid.h"
 #include "nc.h"
6ac1181e
 #include "ot_nonce.h"
b3f0a3f0
 #include "rfc2617.h"
e7faf1ee
 #include "rfc2617_sha256.h"
eef11a9f
 
ffc62e55
 MODULE_VERSION
 
415406e8
 #define RAND_SECRET_LEN 32
2d429fbd
 
8477b7f3
 
462def1b
 /*
  * Module destroy function prototype
  */
acd3e9c7
 static void destroy(void);
462def1b
 
 /*
  * Module initialization function prototype
  */
acd3e9c7
 static int mod_init(void);
36bd09d1
 
462def1b
 /*
8ee734e1
  * Remove used credentials from a SIP message header
462def1b
  */
dc15caf8
 int w_consume_credentials(struct sip_msg* msg, char* s1, char* s2);
1a0d7653
 /*
  * Check for credentials with given realm
  */
 int w_has_credentials(struct sip_msg* msg, char* s1, char* s2);
5d34d82e
 
b3f0a3f0
 static int pv_proxy_authenticate(struct sip_msg* msg, char* realm,
 		char *passwd, char *flags);
 static int pv_www_authenticate(struct sip_msg* msg, char* realm,
 		char *passwd, char *flags);
3f35106f
 static int pv_www_authenticate2(struct sip_msg* msg, char* realm,
 		char *passwd, char *flags, char *method);
b3f0a3f0
 static int fixup_pv_auth(void **param, int param_no);
4298b94f
 static int w_pv_auth_check(sip_msg_t *msg, char *realm,
8de2a607
 		char *passwd, char *flags, char *checks);
 static int fixup_pv_auth_check(void **param, int param_no);
b3f0a3f0
 
 static int proxy_challenge(struct sip_msg *msg, char* realm, char *flags);
 static int www_challenge(struct sip_msg *msg, char* realm, char *flags);
ef6bb454
 static int w_auth_challenge(struct sip_msg *msg, char* realm, char *flags);
b3f0a3f0
 static int fixup_auth_challenge(void **param, int param_no);
 
bcae9bab
 static int w_auth_get_www_authenticate(sip_msg_t* msg, char* realm,
 		char *flags, char *dst);
 static int fixup_auth_get_www_authenticate(void **param, int param_no);
462def1b
 
 /*
  * Module parameter variables
  */
f6beb702
 char* sec_param    = 0;     /* If the parameter was not used, the secret phrase will be auto-generated */
 int   nonce_expire = 300;   /* Nonce lifetime */
b3fde3a6
 /*int   auth_extra_checks = 0;  -- in nonce.c */
f6beb702
 int   protect_contacts = 0; /* Do not include contacts in nonce by default */
c53bc979
 int force_stateless_reply = 0; /* Always send reply statelessly */
0ea2f067
 
00d758fb
 /*! Prefix to strip from realm */
 str auth_realm_prefix = {"", 0};
 
8de2a607
 static int auth_use_domain = 0;
 
b3fde3a6
 str secret1;
 str secret2;
 char* sec_rand1 = 0;
 char* sec_rand2 = 0;
462def1b
 
8ee734e1
 str challenge_attr = STR_STATIC_INIT("$digest_challenge");
 avp_ident_t challenge_avpid;
 
 str proxy_challenge_header = STR_STATIC_INIT("Proxy-Authenticate");
 str www_challenge_header = STR_STATIC_INIT("WWW-Authenticate");
 
b3f0a3f0
 struct qp auth_qop = {
c4b6428c
 	STR_STATIC_INIT("auth"),
 	QOP_AUTH
8ee734e1
 };
 
b3f0a3f0
 static struct qp auth_qauth = {
c4b6428c
 	STR_STATIC_INIT("auth"),
 	QOP_AUTH
b3f0a3f0
 };
 
 static struct qp auth_qauthint = {
c4b6428c
 	STR_STATIC_INIT("auth-int"),
 	QOP_AUTHINT
b3f0a3f0
 };
 
e7faf1ee
 /* Hash algorithm used for digest authentication, MD5 if empty */
 str auth_algorithm = {"", 0};
 int hash_hex_len;
e2ee8b57
 int add_authinfo_hdr = 0; /* should an Authentication-Info header be added on 200 OK responses? */
e7faf1ee
 
 calc_HA1_t calc_HA1;
 calc_response_t calc_response;
 
 
b3f0a3f0
 /*! SL API structure */
 sl_api_t slb;
8d661a85
 
79bee100
 /*
6f43dd16
  * Exported functions
79bee100
  */
 static cmd_export_t cmds[] = {
c4b6428c
 	{"consume_credentials",    w_consume_credentials,                0,
fa509adb
 		0, 0, REQUEST_ROUTE},
c4b6428c
 	{"www_challenge",          (cmd_function)www_challenge,          2,
fa509adb
 		fixup_auth_challenge, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
c4b6428c
 	{"proxy_challenge",        (cmd_function)proxy_challenge,        2,
fa509adb
 		fixup_auth_challenge, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
c4b6428c
 	{"auth_challenge",         (cmd_function)w_auth_challenge,       2,
fa509adb
 		fixup_auth_challenge, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
c4b6428c
 	{"pv_www_authorize",       (cmd_function)pv_www_authenticate,    3,
fa509adb
 		fixup_pv_auth, 0, REQUEST_ROUTE},
c4b6428c
 	{"pv_www_authenticate",    (cmd_function)pv_www_authenticate,    3,
fa509adb
 		fixup_pv_auth, 0, REQUEST_ROUTE},
c4b6428c
 	{"pv_www_authenticate",    (cmd_function)pv_www_authenticate2,   4,
fa509adb
 		fixup_pv_auth, 0, REQUEST_ROUTE},
c4b6428c
 	{"pv_proxy_authorize",     (cmd_function)pv_proxy_authenticate,  3,
fa509adb
 		fixup_pv_auth, 0, REQUEST_ROUTE},
c4b6428c
 	{"pv_proxy_authenticate",  (cmd_function)pv_proxy_authenticate,  3,
fa509adb
 		fixup_pv_auth, 0, REQUEST_ROUTE},
c4b6428c
 	{"auth_get_www_authenticate",  (cmd_function)w_auth_get_www_authenticate,  3,
fa509adb
 		fixup_auth_get_www_authenticate, 0, REQUEST_ROUTE},
c4b6428c
 	{"has_credentials",        w_has_credentials,                    1,
fa509adb
 		fixup_spve_null, 0, REQUEST_ROUTE},
4298b94f
 	{"pv_auth_check",         (cmd_function)w_pv_auth_check,           4,
fa509adb
 		fixup_pv_auth_check, 0, REQUEST_ROUTE},
c4b6428c
 	{"bind_auth_s",           (cmd_function)bind_auth_s, 0, 0, 0        },
fa509adb
 	{0, 0, 0, 0, 0, 0}
79bee100
 };
 
 
 /*
  * Exported parameters
  */
 static param_export_t params[] = {
c4b6428c
 	{"secret",                 PARAM_STRING, &sec_param             },
 	{"nonce_expire",           PARAM_INT,    &nonce_expire          },
2e3672f1
 	{"nonce_auth_max_drift",   PARAM_INT,    &nonce_auth_max_drift  },
c4b6428c
 	{"protect_contacts",       PARAM_INT,    &protect_contacts      },
 	{"challenge_attr",         PARAM_STR,    &challenge_attr        },
 	{"proxy_challenge_header", PARAM_STR,    &proxy_challenge_header},
 	{"www_challenge_header",   PARAM_STR,    &www_challenge_header  },
 	{"qop",                    PARAM_STR,    &auth_qop.qop_str      },
ae29ff90
 	{"auth_checks_register",   PARAM_INT,    &auth_checks_reg       },
 	{"auth_checks_no_dlg",     PARAM_INT,    &auth_checks_ood       },
 	{"auth_checks_in_dlg",     PARAM_INT,    &auth_checks_ind       },
6ac1181e
 	{"nonce_count"  ,          PARAM_INT,    &nc_enabled            },
12690ddb
 	{"nc_array_size",          PARAM_INT,    &nc_array_size         },
 	{"nc_array_order",         PARAM_INT,    &nc_array_k            },
6ac1181e
 	{"one_time_nonce"  ,       PARAM_INT,    &otn_enabled           },
 	{"otn_in_flight_no",       PARAM_INT,    &otn_in_flight_no      },
 	{"otn_in_flight_order",    PARAM_INT,    &otn_in_flight_k       },
8de2a607
 	{"nid_pool_no",            PARAM_INT,    &nid_pool_no           },
c4b6428c
 	{"force_stateless_reply",  PARAM_INT,    &force_stateless_reply },
00d758fb
 	{"realm_prefix",           PARAM_STRING, &auth_realm_prefix.s   },
c4b6428c
 	{"use_domain",             PARAM_INT,    &auth_use_domain       },
e7faf1ee
 	{"algorithm",              PARAM_STR,    &auth_algorithm        },
e2ee8b57
 	{"add_authinfo_hdr",       INT_PARAM,    &add_authinfo_hdr      },
c4b6428c
 	{0, 0, 0}
79bee100
 };
 
 
462def1b
 /*
  * Module interface
  */
acd3e9c7
 struct module_exports exports = {
c4b6428c
 	"auth",
fa509adb
 	DEFAULT_DLFLAGS, /* dlopen flags */
c4b6428c
 	cmds,
 	params,
fa509adb
 	0,          /* RPC methods */
         0,          /* pseudo-variables exports */
c4b6428c
 	0,          /* response function */
fa509adb
 	mod_init,   /* module initialization function */
 	0,          /* child initialization function */
 	destroy     /* destroy function */
2d429fbd
 };
 
 
1f31c4bf
 /*
  * Secret parameter was not used so we generate
  * a random value here
  */
 static inline int generate_random_secret(void)
 {
c4b6428c
 	int i;
 
 	sec_rand1 = (char*)pkg_malloc(RAND_SECRET_LEN);
 	sec_rand2 = (char*)pkg_malloc(RAND_SECRET_LEN);
 	if (!sec_rand1 || !sec_rand2) {
1a5cb034
 		LM_ERR("No memory left\n");
c4b6428c
 		if (sec_rand1){
 			pkg_free(sec_rand1);
 			sec_rand1=0;
 		}
 		return -1;
b3fde3a6
 	}
c4b6428c
 
 	for(i = 0; i < RAND_SECRET_LEN; i++) {
5836b40f
 		sec_rand1[i] = 32 + (int)(95.0 * kam_rand() / (KAM_RAND_MAX + 1.0));
c4b6428c
 	}
 
 	secret1.s = sec_rand1;
 	secret1.len = RAND_SECRET_LEN;
 
 	for(i = 0; i < RAND_SECRET_LEN; i++) {
5836b40f
 		sec_rand2[i] = 32 + (int)(95.0 * kam_rand() / (KAM_RAND_MAX + 1.0));
c4b6428c
 	}
 
 	secret2.s = sec_rand2;
 	secret2.len = RAND_SECRET_LEN;
 
 	/* DBG("Generated secret: '%.*s'\n", secret.len, secret.s); */
 
 	return 0;
1f31c4bf
 }
 
 
acd3e9c7
 static int mod_init(void)
2d429fbd
 {
c4b6428c
 	str attr;
 
 	DBG("auth module - initializing\n");
 
00d758fb
 	auth_realm_prefix.len = strlen(auth_realm_prefix.s);
 
b3f0a3f0
 	/* bind the SL API */
 	if (sl_load_api(&slb)!=0) {
 		LM_ERR("cannot bind to SL API\n");
 		return -1;
 	}
 
ae29ff90
 	/* If the parameter was not used */
c4b6428c
 	if (sec_param == 0) {
ae29ff90
 		/* Generate secret using random generator */
 		if (generate_random_secret() < 0) {
1a5cb034
 			LM_ERR("Error while generating random secret\n");
ae29ff90
 			return -3;
 		}
c4b6428c
 	} else {
ae29ff90
 		/* Otherwise use the parameter's value */
 		secret1.s = sec_param;
 		secret1.len = strlen(secret1.s);
c4b6428c
 
ae29ff90
 		if (auth_checks_reg || auth_checks_ind || auth_checks_ood) {
 			/* divide the secret in half: one half for secret1 and one half for
 			 *  secret2 */
 			secret2.len = secret1.len/2;
 			secret1.len -= secret2.len;
 			secret2.s = secret1.s + secret1.len;
 			if (secret2.len < 16) {
1a5cb034
 				LM_WARN("consider a longer secret when extra auth checks are"
c4b6428c
 						" enabled (the config secret is divided in 2!)\n");
ae29ff90
 			}
b3fde3a6
 		}
c4b6428c
 	}
 
 	if ((!challenge_attr.s || challenge_attr.len == 0) ||
 			challenge_attr.s[0] != '$') {
1a5cb034
 		LM_ERR("Invalid value of challenge_attr module parameter\n");
ae29ff90
 		return -1;
c4b6428c
 	}
 
 	attr.s = challenge_attr.s + 1;
 	attr.len = challenge_attr.len - 1;
 
 	if (parse_avp_ident(&attr, &challenge_avpid) < 0) {
1a5cb034
 		LM_ERR("Error while parsing value of challenge_attr module"
b3f0a3f0
 				" parameter\n");
ae29ff90
 		return -1;
c4b6428c
 	}
 
 	parse_qop(&auth_qop);
b3f0a3f0
 	switch(auth_qop.qop_parsed){
12690ddb
 		case QOP_OTHER:
1a5cb034
 			LM_ERR("Unsupported qop parameter value\n");
12690ddb
 			return -1;
 		case QOP_AUTH:
 		case QOP_AUTHINT:
 			if (nc_enabled){
 #ifndef USE_NC
1a5cb034
 				LM_WARN("nounce count support enabled from config, but"
c4b6428c
 						" disabled at compile time (recompile with -DUSE_NC)\n");
6ac1181e
 				nc_enabled=0;
12690ddb
 #else
 				if (nid_crt==0)
 					init_nonce_id();
6ac1181e
 				if (init_nonce_count()!=0)
 					return -1;
12690ddb
 #endif
 			}
 #ifdef USE_NC
 			else{
75ef2a31
 				LM_INFO("qop set, but nonce-count (nonce_count) support"
12690ddb
 						" disabled\n");
 			}
 #endif
 			break;
 		default:
6ac1181e
 			if (nc_enabled){
1a5cb034
 				LM_WARN("nonce-count support enabled, but qop not set\n");
6ac1181e
 				nc_enabled=0;
 			}
12690ddb
 			break;
 	}
6ac1181e
 	if (otn_enabled){
 #ifdef USE_OT_NONCE
 		if (nid_crt==0) init_nonce_id();
c4b6428c
 		if (init_ot_nonce()!=0)
6ac1181e
 			return -1;
 #else
1a5cb034
 		LM_WARN("one-time-nonce support enabled from config, but "
6ac1181e
 				"disabled at compile time (recompile with -DUSE_OT_NONCE)\n");
 		otn_enabled=0;
 #endif /* USE_OT_NONCE */
 	}
12690ddb
 
e7faf1ee
 	if (auth_algorithm.len == 0 || strcmp(auth_algorithm.s, "MD5") == 0) {
 		hash_hex_len = HASHHEXLEN;
 		calc_HA1 = calc_HA1_md5;
 		calc_response = calc_response_md5;
 	}
 	else if (strcmp(auth_algorithm.s, "SHA-256") == 0) {
 		hash_hex_len = HASHHEXLEN_SHA256;
 		calc_HA1 = calc_HA1_sha256;
 		calc_response = calc_response_sha256;
 	}
 	else {
1a5cb034
 		LM_ERR("Invalid algorithm provided."
 				" Possible values are \"\", \"MD5\" or \"SHA-256\"\n");
e7faf1ee
 		return -1;
 	}
 
c4b6428c
 	return 0;
2d429fbd
 }
 
 
acd3e9c7
 static void destroy(void)
2d429fbd
 {
c4b6428c
 	if (sec_rand1) pkg_free(sec_rand1);
 	if (sec_rand2) pkg_free(sec_rand2);
12690ddb
 #ifdef USE_NC
 	destroy_nonce_count();
6ac1181e
 #endif
 #ifdef USE_OT_NONCE
 	destroy_ot_nonce();
 #endif
 #if defined USE_NC || defined USE_OT_NONCE
12690ddb
 	destroy_nonce_id();
 #endif
2d429fbd
 }
462def1b
 
 
8ee734e1
 /*
  * Remove used credentials from a SIP message header
  */
dc15caf8
 int consume_credentials(struct sip_msg* msg)
462def1b
 {
c4b6428c
 	struct hdr_field* h;
 	int len;
2a77ed2b
 
 	/* skip requests that can't be authenticated */
 	if (msg->REQ_METHOD & (METHOD_ACK|METHOD_CANCEL|METHOD_PRACK))
 		return -1;
c4b6428c
 	get_authorized_cred(msg->authorization, &h);
 	if (!h) {
ae29ff90
 		get_authorized_cred(msg->proxy_auth, &h);
c4b6428c
 		if (!h) {
1a5cb034
 			LM_ERR("No authorized credentials found (error in scripts)\n");
ae29ff90
 			return -1;
 		}
c4b6428c
 	}
 
 	len = h->len;
 
 	if (del_lump(msg, h->name.s - msg->buf, len, 0) == 0) {
1a5cb034
 		LM_ERR("Can't remove credentials\n");
ae29ff90
 		return -1;
c4b6428c
 	}
 
 	return 1;
462def1b
 }
8ee734e1
 
dc15caf8
 /**
  *
  */
 int w_consume_credentials(struct sip_msg* msg, char* s1, char* s2)
 {
 	return consume_credentials(msg);
 }
 
1a0d7653
 /**
  *
  */
3ce13be0
 int ki_has_credentials(sip_msg_t *msg, str* srealm)
1a0d7653
 {
 	hdr_field_t *hdr = NULL;
 	int ret;
 
3ce13be0
 	ret = find_credentials(msg, srealm, HDR_PROXYAUTH_T, &hdr);
1a0d7653
 	if(ret==0) {
3839eed2
 		LM_DBG("found proxy credentials with realm [%.*s]\n", srealm->len, srealm->s);
1a0d7653
 		return 1;
 	}
3ce13be0
 	ret = find_credentials(msg, srealm, HDR_AUTHORIZATION_T, &hdr);
1a0d7653
 	if(ret==0) {
3839eed2
 		LM_DBG("found www credentials with realm [%.*s]\n", srealm->len, srealm->s);
1a0d7653
 		return 1;
 	}
 
3ce13be0
 	LM_DBG("no credentials with realm [%.*s]\n", srealm->len, srealm->s);
1a0d7653
 	return -1;
 }
 
3ce13be0
 /**
  *
  */
 int w_has_credentials(sip_msg_t *msg, char* realm, char* s2)
 {
 	str srealm  = {0, 0};
 
 	if (fixup_get_svalue(msg, (gparam_t*)realm, &srealm) < 0) {
 		LM_ERR("failed to get realm value\n");
 		return -1;
 	}
 	return ki_has_credentials(msg, &srealm);
 }
b3f0a3f0
 /**
  * @brief do WWW-Digest authentication with password taken from cfg var
  */
dc15caf8
 int pv_authenticate(struct sip_msg *msg, str *realm, str *passwd,
3f35106f
 		int flags, int hftype, str *method)
b3f0a3f0
 {
 	struct hdr_field* h;
 	auth_body_t* cred;
41e9af58
 	auth_cfg_result_t ret;
 	auth_result_t rauth;
c4b6428c
 	str hf = {0, 0};
 	avp_value_t val;
b3f0a3f0
 	static char ha1[256];
 	struct qp *qop = NULL;
 
 	cred = 0;
9c2c336d
 	ret = AUTH_ERROR;
b3f0a3f0
 
5674d659
 	switch(pre_auth(msg, realm, hftype, &h, NULL)) {
5ec0e422
 		case NONCE_REUSED:
 			LM_DBG("nonce reused");
 			ret = AUTH_NONCE_REUSED;
 			goto end;
 		case STALE_NONCE:
 			LM_DBG("stale nonce\n");
 			ret = AUTH_STALE_NONCE;
 			goto end;
 		case NO_CREDENTIALS:
 			LM_DBG("no credentials\n");
 			ret = AUTH_NO_CREDENTIALS;
 			goto end;
b3f0a3f0
 		case ERROR:
 		case BAD_CREDENTIALS:
 			LM_DBG("error or bad credentials\n");
9c2c336d
 			ret = AUTH_ERROR;
b3f0a3f0
 			goto end;
 		case CREATE_CHALLENGE:
 			LM_ERR("CREATE_CHALLENGE is not a valid state\n");
9c2c336d
 			ret = AUTH_ERROR;
b3f0a3f0
 			goto end;
 		case DO_RESYNCHRONIZATION:
 			LM_ERR("DO_RESYNCHRONIZATION is not a valid state\n");
9c2c336d
 			ret = AUTH_ERROR;
b3f0a3f0
 			goto end;
 		case NOT_AUTHENTICATED:
 			LM_DBG("not authenticated\n");
9c2c336d
 			ret = AUTH_ERROR;
b3f0a3f0
 			goto end;
 		case DO_AUTHENTICATION:
 			break;
 		case AUTHENTICATED:
9c2c336d
 			ret = AUTH_OK;
b3f0a3f0
 			goto end;
 	}
 
 	cred = (auth_body_t*)h->parsed;
 
 	/* compute HA1 if needed */
 	if ((flags&1)==0) {
 		/* Plaintext password is stored in PV, calculate HA1 */
5674d659
 		calc_HA1(HA_MD5, &cred->digest.username.whole, realm,
 				passwd, 0, 0, ha1);
b3f0a3f0
 		LM_DBG("HA1 string calculated: %s\n", ha1);
 	} else {
5674d659
 		memcpy(ha1, passwd->s, passwd->len);
 		ha1[passwd->len] = '\0';
b3f0a3f0
 	}
 
 	/* Recalculate response, it must be same to authorize successfully */
41e9af58
 	rauth = auth_check_response(&(cred->digest), method, ha1);
 	if(rauth==AUTHENTICATED) {
9c2c336d
 		ret = AUTH_OK;
e2ee8b57
 		switch(post_auth(msg, h, ha1)) {
b3f0a3f0
 			case AUTHENTICATED:
 				break;
 			default:
9c2c336d
 				ret = AUTH_ERROR;
b3f0a3f0
 				break;
 		}
 	} else {
41e9af58
 		if(rauth==NOT_AUTHENTICATED)
9c2c336d
 			ret = AUTH_INVALID_PASSWORD;
 		else
 			ret = AUTH_ERROR;
b3f0a3f0
 	}
 
9c2c336d
 end:
b3f0a3f0
 	if (ret < 0) {
 		/* check if required to add challenge header as avp */
 		if(!(flags&14))
 			return ret;
 		if(flags&8) {
 			qop = &auth_qauthint;
 		} else if(flags&4) {
 			qop = &auth_qauth;
 		}
 		if (get_challenge_hf(msg, (cred ? cred->stale : 0),
e7faf1ee
 					realm, NULL, (auth_algorithm.len ? &auth_algorithm : NULL), qop, hftype, &hf) < 0) {
1a5cb034
 			LM_ERR("Error while creating challenge\n");
9c2c336d
 			ret = AUTH_ERROR;
b3f0a3f0
 		} else {
 			val.s = hf;
 			if(add_avp(challenge_avpid.flags | AVP_VAL_STR,
c4b6428c
 						challenge_avpid.name, val) < 0) {
b3f0a3f0
 				LM_ERR("Error while creating attribute with challenge\n");
9c2c336d
 				ret = AUTH_ERROR;
b3f0a3f0
 			}
 			pkg_free(hf.s);
 		}
 	}
 
9c2c336d
 	return ret;
b3f0a3f0
 }
 
 /**
  *
  */
 static int pv_proxy_authenticate(struct sip_msg *msg, char* realm,
 		char *passwd, char *flags)
 {
c4b6428c
 	int vflags = 0;
 	str srealm  = {0, 0};
 	str spasswd = {0, 0};
5674d659
 
 	if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
 		LM_ERR("failed to get realm value\n");
 		goto error;
 	}
 
 	if(srealm.len==0) {
 		LM_ERR("invalid realm value - empty content\n");
 		goto error;
 	}
 
 	if (get_str_fparam(&spasswd, msg, (fparam_t*)passwd) < 0) {
 		LM_ERR("failed to get passwd value\n");
 		goto error;
 	}
 
 	if(spasswd.len==0) {
 		LM_ERR("invalid password value - empty content\n");
 		goto error;
 	}
 
 	if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
 		LM_ERR("invalid flags value\n");
 		goto error;
 	}
3f35106f
 	return pv_authenticate(msg, &srealm, &spasswd, vflags, HDR_PROXYAUTH_T,
c4b6428c
 			&msg->first_line.u.request.method);
5674d659
 
 error:
 	return AUTH_ERROR;
b3f0a3f0
 }
 
 /**
  *
  */
 static int pv_www_authenticate(struct sip_msg *msg, char* realm,
 		char *passwd, char *flags)
 {
c4b6428c
 	int vflags = 0;
 	str srealm  = {0, 0};
 	str spasswd = {0, 0};
5674d659
 
 	if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
 		LM_ERR("failed to get realm value\n");
 		goto error;
 	}
 
 	if(srealm.len==0) {
 		LM_ERR("invalid realm value - empty content\n");
 		goto error;
 	}
 
 	if (get_str_fparam(&spasswd, msg, (fparam_t*)passwd) < 0) {
 		LM_ERR("failed to get passwd value\n");
 		goto error;
 	}
 
 	if(spasswd.len==0) {
 		LM_ERR("invalid password value - empty content\n");
 		goto error;
 	}
 
 	if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
 		LM_ERR("invalid flags value\n");
 		goto error;
 	}
3f35106f
 	return pv_authenticate(msg, &srealm, &spasswd, vflags, HDR_AUTHORIZATION_T,
c4b6428c
 			&msg->first_line.u.request.method);
3f35106f
 
 error:
 	return AUTH_ERROR;
 }
 
 static int pv_www_authenticate2(struct sip_msg *msg, char* realm,
 		char *passwd, char *flags, char *method)
 {
c4b6428c
 	int vflags = 0;
 	str srealm  = {0, 0};
 	str spasswd = {0, 0};
 	str smethod = {0, 0};
3f35106f
 
 	if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
 		LM_ERR("failed to get realm value\n");
 		goto error;
 	}
 
 	if(srealm.len==0) {
 		LM_ERR("invalid realm value - empty content\n");
 		goto error;
 	}
 
 	if (get_str_fparam(&spasswd, msg, (fparam_t*)passwd) < 0) {
 		LM_ERR("failed to get passwd value\n");
 		goto error;
 	}
 
 	if(spasswd.len==0) {
 		LM_ERR("invalid password value - empty content\n");
 		goto error;
 	}
 
 	if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
 		LM_ERR("invalid flags value\n");
 		goto error;
 	}
 
 	if (get_str_fparam(&smethod, msg, (fparam_t*)method) < 0) {
35329870
 		LM_ERR("failed to get method value from msg %p var %p\n", msg, method);
3f35106f
 		goto error;
 	}
 
 	if(smethod.len==0) {
 		LM_ERR("invalid method value - empty content\n");
 		goto error;
 	}
 
 	return pv_authenticate(msg, &srealm, &spasswd, vflags, HDR_AUTHORIZATION_T,
c4b6428c
 			&smethod);
5674d659
 
 error:
 	return AUTH_ERROR;
b3f0a3f0
 }
 
8de2a607
 /**
  *
  */
4298b94f
 static int pv_auth_check(sip_msg_t *msg, str *srealm, str *spasswd, int vflags,
 		int vchecks)
8de2a607
 {
 	int ret;
 	hdr_field_t *hdr;
 	sip_uri_t *uri = NULL;
 	sip_uri_t *turi = NULL;
 	sip_uri_t *furi = NULL;
4298b94f
 	str suser;
8de2a607
 
 	if(msg->REQ_METHOD==METHOD_REGISTER)
4298b94f
 		ret = pv_authenticate(msg, srealm, spasswd, vflags, HDR_AUTHORIZATION_T,
c4b6428c
 				&msg->first_line.u.request.method);
8de2a607
 	else
4298b94f
 		ret = pv_authenticate(msg, srealm, spasswd, vflags, HDR_PROXYAUTH_T,
c4b6428c
 				&msg->first_line.u.request.method);
8de2a607
 
2cdda10a
 	if(ret==AUTH_OK && (vchecks&AUTH_CHECK_ID_F)) {
8de2a607
 		hdr = (msg->proxy_auth==0)?msg->authorization:msg->proxy_auth;
ea2ea2f8
 		if(hdr==NULL) {
 			if (msg->REQ_METHOD & (METHOD_ACK|METHOD_CANCEL|METHOD_PRACK)) {
 				return AUTH_OK;
 			} else {
 				return AUTH_ERROR;
 			}
 		}
4298b94f
 		suser = ((auth_body_t*)(hdr->parsed))->digest.username.user;
8de2a607
 
 		if((furi=parse_from_uri(msg))==NULL)
 			return AUTH_ERROR;
 
 		if(msg->REQ_METHOD==METHOD_REGISTER || msg->REQ_METHOD==METHOD_PUBLISH) {
 			if((turi=parse_to_uri(msg))==NULL)
 				return AUTH_ERROR;
 			uri = turi;
 		} else {
 			uri = furi;
 		}
4298b94f
 		if(suser.len!=uri->user.len
 				|| strncmp(suser.s, uri->user.s, suser.len)!=0)
8de2a607
 			return AUTH_USER_MISMATCH;
 
 		if(msg->REQ_METHOD==METHOD_REGISTER || msg->REQ_METHOD==METHOD_PUBLISH) {
 			/* check from==to */
 			if(furi->user.len!=turi->user.len
 					|| strncmp(furi->user.s, turi->user.s, furi->user.len)!=0)
 				return AUTH_USER_MISMATCH;
 			if(auth_use_domain!=0 && (furi->host.len!=turi->host.len
c4b6428c
 						|| strncmp(furi->host.s, turi->host.s, furi->host.len)!=0))
8de2a607
 				return AUTH_USER_MISMATCH;
 			/* check r-uri==from for publish */
 			if(msg->REQ_METHOD==METHOD_PUBLISH) {
 				if(parse_sip_msg_uri(msg)<0)
 					return AUTH_ERROR;
 				uri = &msg->parsed_uri;
 				if(furi->user.len!=uri->user.len
 						|| strncmp(furi->user.s, uri->user.s, furi->user.len)!=0)
 					return AUTH_USER_MISMATCH;
 				if(auth_use_domain!=0 && (furi->host.len!=uri->host.len
c4b6428c
 							|| strncmp(furi->host.s, uri->host.s, furi->host.len)!=0))
8de2a607
 					return AUTH_USER_MISMATCH;
c4b6428c
 			}
8de2a607
 		}
 		return AUTH_OK;
 	}
 
 	return ret;
 }
 
4298b94f
 /**
  *
  */
 static int w_pv_auth_check(sip_msg_t *msg, char *realm,
 		char *passwd, char *flags, char *checks)
 {
 	int vflags = 0;
 	int vchecks = 0;
 	str srealm  = {0, 0};
 	str spasswd = {0, 0};
 
 
 	if(msg==NULL) {
 		LM_ERR("invalid msg parameter\n");
 		return AUTH_ERROR;
 	}
 
 	if ((msg->REQ_METHOD == METHOD_ACK) || (msg->REQ_METHOD == METHOD_CANCEL)) {
 		return AUTH_OK;
 	}
 
 	if(realm==NULL || passwd==NULL || flags==NULL || checks==NULL) {
 		LM_ERR("invalid parameters\n");
 		return AUTH_ERROR;
 	}
 
 	if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
 		LM_ERR("failed to get realm value\n");
 		return AUTH_ERROR;
 	}
 
 	if(srealm.len==0) {
 		LM_ERR("invalid realm value - empty content\n");
 		return AUTH_ERROR;
 	}
 
 	if (get_str_fparam(&spasswd, msg, (fparam_t*)passwd) < 0) {
 		LM_ERR("failed to get passwd value\n");
 		return AUTH_ERROR;
 	}
 
 	if(spasswd.len==0) {
 		LM_ERR("invalid password value - empty content\n");
 		return AUTH_ERROR;
 	}
 
 	if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
 		LM_ERR("invalid flags value\n");
 		return AUTH_ERROR;
 	}
 
 	if (get_int_fparam(&vchecks, msg, (fparam_t*)checks) < 0) {
 		LM_ERR("invalid checks value\n");
 		return AUTH_ERROR;
 	}
 	LM_DBG("realm [%.*s] flags [%d] checks [%d]\n", srealm.len, srealm.s,
 			vflags, vchecks);
 	return pv_auth_check(msg, &srealm, &spasswd, vflags, vchecks);
 }
 
 
b3f0a3f0
 /**
  * @brief fixup function for pv_{www,proxy}_authenticate
  */
 static int fixup_pv_auth(void **param, int param_no)
 {
9c2c336d
 	if(strlen((char*)*param)<=0) {
 		LM_ERR("empty parameter %d not allowed\n", param_no);
 		return -1;
 	}
 
b3f0a3f0
 	switch(param_no) {
 		case 1:
 		case 2:
dd00819f
 		case 4:
47f3c43c
 			return fixup_var_pve_str_12(param, 1);
b3f0a3f0
 		case 3:
 			return fixup_var_int_12(param, 1);
 	}
 	return 0;
 }
 
8de2a607
 /**
  * @brief fixup function for pv_{www,proxy}_authenticate
  */
 static int fixup_pv_auth_check(void **param, int param_no)
 {
 	if(strlen((char*)*param)<=0) {
 		LM_ERR("empty parameter %d not allowed\n", param_no);
 		return -1;
 	}
 
 	switch(param_no) {
 		case 1:
 		case 2:
 			return fixup_var_pve_str_12(param, 1);
 		case 3:
 		case 4:
 			return fixup_var_int_12(param, 1);
 	}
 	return 0;
 }
 
 
b3f0a3f0
 
 /**
  *
  */
 static int auth_send_reply(struct sip_msg *msg, int code, char *reason,
c4b6428c
 		char *hdr, int hdr_len)
b3f0a3f0
 {
c4b6428c
 	str reason_str;
c53bc979
 
b3f0a3f0
 	/* Add new headers if there are any */
 	if ((hdr!=NULL) && (hdr_len>0)) {
 		if (add_lump_rpl(msg, hdr, hdr_len, LUMP_RPL_HDR)==0) {
 			LM_ERR("failed to append hdr to reply\n");
 			return -1;
 		}
 	}
 
c53bc979
 	reason_str.s = reason;
 	reason_str.len = strlen(reason);
 
 	return force_stateless_reply ?
c4b6428c
 		slb.sreply(msg, code, &reason_str) :
 		slb.freply(msg, code, &reason_str);
b3f0a3f0
 }
 
 /**
  *
  */
bcae9bab
 int auth_challenge_helper(struct sip_msg *msg, str *realm, int flags, int hftype,
 		str *res)
b3f0a3f0
 {
c4b6428c
 	int ret, stale;
 	str hf = {0, 0};
b3f0a3f0
 	struct qp *qop = NULL;
 
 	ret = -1;
 
 	if(flags&2) {
 		qop = &auth_qauthint;
 	} else if(flags&1) {
 		qop = &auth_qauth;
 	}
b63f14b1
 	if (flags & 16) {
c4b6428c
 		stale = 1;
b63f14b1
 	} else {
c4b6428c
 		stale = 0;
b63f14b1
 	}
e7faf1ee
 	if (get_challenge_hf(msg, stale, realm, NULL, (auth_algorithm.len ? &auth_algorithm : NULL), qop, hftype, &hf)
c4b6428c
 			< 0) {
1a5cb034
 		LM_ERR("Error while creating challenge\n");
b3f0a3f0
 		ret = -2;
 		goto error;
 	}
c4b6428c
 
b3f0a3f0
 	ret = 1;
bcae9bab
 	if(res!=NULL)
 	{
 		*res = hf;
 		return ret;
 	}
b3f0a3f0
 	switch(hftype) {
 		case HDR_AUTHORIZATION_T:
 			if(auth_send_reply(msg, 401, "Unauthorized",
 						hf.s, hf.len) <0 )
 				ret = -3;
c4b6428c
 			break;
b3f0a3f0
 		case HDR_PROXYAUTH_T:
 			if(auth_send_reply(msg, 407, "Proxy Authentication Required",
 						hf.s, hf.len) <0 )
 				ret = -3;
c4b6428c
 			break;
b3f0a3f0
 	}
 	if(hf.s) pkg_free(hf.s);
 	return ret;
 
 error:
 	if(hf.s) pkg_free(hf.s);
 	if(!(flags&4)) {
 		if(auth_send_reply(msg, 500, "Internal Server Error", 0, 0) <0 )
 			ret = -4;
 	}
 	return ret;
 }
 
bcae9bab
 /**
  *
  */
019637af
 int auth_challenge_hftype(struct sip_msg *msg, str *realm, int flags, int hftype)
bcae9bab
 {
 	return auth_challenge_helper(msg, realm, flags, hftype, NULL);
 }
 
019637af
 /**
  *
  */
 int auth_challenge(sip_msg_t *msg, str *realm, int flags)
 {
 	int htype;
 
 	if(msg==NULL) return -1;
 
 	if(msg->REQ_METHOD==METHOD_REGISTER)
 		htype = HDR_AUTHORIZATION_T;
 	else
 		htype = HDR_PROXYAUTH_T;
 
 	return auth_challenge_helper(msg, realm, flags, htype, NULL);
 }
 
b3f0a3f0
 /**
  *
  */
 static int proxy_challenge(struct sip_msg *msg, char* realm, char *flags)
 {
e5f14b9e
 	int vflags = 0;
 	str srealm  = {0, 0};
 
 	if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
 		LM_ERR("failed to get realm value\n");
 		goto error;
 	}
 
 	if(srealm.len==0) {
 		LM_ERR("invalid realm value - empty content\n");
 		goto error;
 	}
 
 	if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
 		LM_ERR("invalid flags value\n");
 		goto error;
 	}
 
019637af
 	return auth_challenge_hftype(msg, &srealm, vflags, HDR_PROXYAUTH_T);
e5f14b9e
 
 error:
 	if(!(vflags&4)) {
 		if(auth_send_reply(msg, 500, "Internal Server Error", 0, 0) <0 )
 			return -4;
 	}
 	return -1;
b3f0a3f0
 }
 
 /**
  *
  */
 static int www_challenge(struct sip_msg *msg, char* realm, char *flags)
 {
e5f14b9e
 	int vflags = 0;
 	str srealm  = {0, 0};
 
 	if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
 		LM_ERR("failed to get realm value\n");
 		goto error;
 	}
 
 	if(srealm.len==0) {
 		LM_ERR("invalid realm value - empty content\n");
 		goto error;
 	}
 
 	if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
 		LM_ERR("invalid flags value\n");
 		goto error;
 	}
 
019637af
 	return auth_challenge_hftype(msg, &srealm, vflags, HDR_AUTHORIZATION_T);
e5f14b9e
 
 error:
 	if(!(vflags&4)) {
 		if(auth_send_reply(msg, 500, "Internal Server Error", 0, 0) <0 )
 			return -4;
 	}
 	return -1;
b3f0a3f0
 }
 
ef6bb454
 /**
  *
  */
 static int w_auth_challenge(struct sip_msg *msg, char* realm, char *flags)
 {
 	int vflags = 0;
 	str srealm  = {0, 0};
 
 	if((msg->REQ_METHOD == METHOD_ACK) || (msg->REQ_METHOD == METHOD_CANCEL)) {
 		return 1;
 	}
 
 	if(get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
 		LM_ERR("failed to get realm value\n");
 		goto error;
 	}
 
 	if(srealm.len==0) {
 		LM_ERR("invalid realm value - empty content\n");
 		goto error;
 	}
 
 	if(get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
 		LM_ERR("invalid flags value\n");
 		goto error;
 	}
 
 	if(msg->REQ_METHOD==METHOD_REGISTER)
019637af
 		return auth_challenge_hftype(msg, &srealm, vflags, HDR_AUTHORIZATION_T);
ef6bb454
 	else
019637af
 		return auth_challenge_hftype(msg, &srealm, vflags, HDR_PROXYAUTH_T);
ef6bb454
 
 error:
 	if(!(vflags&4)) {
 		if(auth_send_reply(msg, 500, "Internal Server Error", 0, 0) <0 )
 			return -4;
 	}
 	return -1;
 }
 
 
b3f0a3f0
 /**
  * @brief fixup function for {www,proxy}_challenge
  */
 static int fixup_auth_challenge(void **param, int param_no)
 {
9c2c336d
 	if(strlen((char*)*param)<=0) {
 		LM_ERR("empty parameter %d not allowed\n", param_no);
 		return -1;
 	}
 
b3f0a3f0
 	switch(param_no) {
 		case 1:
 			return fixup_var_str_12(param, 1);
 		case 2:
 			return fixup_var_int_12(param, 1);
 	}
 	return 0;
 }
bcae9bab
 
 
 /**
  *
  */
 static int w_auth_get_www_authenticate(sip_msg_t* msg, char* realm,
 		char *flags, char *dst)
 {
 	int vflags = 0;
 	str srealm  = {0};
 	str hf = {0};
 	pv_spec_t *pv;
 	pv_value_t val;
 	int ret;
 
 	if(get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
 		LM_ERR("failed to get realm value\n");
 		goto error;
 	}
 
 	if(srealm.len==0) {
 		LM_ERR("invalid realm value - empty content\n");
 		goto error;
 	}
 
 	if(get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
 		LM_ERR("invalid flags value\n");
 		goto error;
 	}
 
 	pv = (pv_spec_t *)dst;
 
 	ret = auth_challenge_helper(NULL, &srealm, vflags,
 			HDR_AUTHORIZATION_T, &hf);
 
 	if(ret<0)
 		return ret;
 
 	val.rs.s = pv_get_buffer();
 	val.rs.len = 0;
 	if(hf.s!=NULL)
 	{
 		memcpy(val.rs.s, hf.s, hf.len);
 		val.rs.len = hf.len;
 		val.rs.s[val.rs.len] = '\0';
 		pkg_free(hf.s);
 	}
 	val.flags = PV_VAL_STR;
 	pv->setf(msg, &pv->pvp, (int)EQ_T, &val);
 
 	return ret;
 
 error:
 	return -1;
 }
 
 
 static int fixup_auth_get_www_authenticate(void **param, int param_no)
 {
 	if(strlen((char*)*param)<=0) {
 		LM_ERR("empty parameter %d not allowed\n", param_no);
 		return -1;
 	}
 
 	switch(param_no) {
 		case 1:
 			return fixup_var_str_12(param, 1);
 		case 2:
 			return fixup_var_int_12(param, 1);
 		case 3:
c4b6428c
 			if (fixup_pvar_null(param, 1) != 0) {
 				LM_ERR("failed to fixup result pvar\n");
 				return -1;
 			}
 			if (((pv_spec_t *)(*param))->setf == NULL) {
 				LM_ERR("result pvar is not writeble\n");
 				return -1;
 			}
 			return 0;
bcae9bab
 	}
 	return 0;
 }
6b0ea231
 
 /**
  *
  */
d39dd548
 /* clang-format off */
6b0ea231
 static sr_kemi_t sr_kemi_auth_exports[] = {
 	{ str_init("auth"), str_init("consume_credentials"),
 		SR_KEMIP_INT, consume_credentials,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("auth"), str_init("auth_challenge"),
 		SR_KEMIP_INT, auth_challenge,
 		{ SR_KEMIP_STR, SR_KEMIP_INT, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("auth"), str_init("pv_auth_check"),
 		SR_KEMIP_INT, pv_auth_check,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_INT,
 			SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
3ce13be0
 	{ str_init("auth"), str_init("has_credentials"),
 		SR_KEMIP_INT, ki_has_credentials,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
6b0ea231
 
 	{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
 };
d39dd548
 /* clang-format on */
6b0ea231
 
 /**
  *
  */
 int mod_register(char *path, int *dlflags, void *p1, void *p2)
 {
 	sr_kemi_modules_add(sr_kemi_auth_exports);
 	return 0;
 }