src/modules/auth_ephemeral/authorize.c
0bea7f63
 /*
  * $Id$
  *
  * Copyright (C) 2013 Crocodile RCS Ltd
765562b2
  * Copyright (C) 2017 ng-voice GmbH
0bea7f63
  *
  * This file is part of Kamailio, a free SIP server.
  *
  * Kamailio is free software; you can redistribute it and/or modify
  * 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
  *
  * Kamailio is distributed in the hope that it will be useful,
  * 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.
  *
  * You should have received a copy of the GNU General Public License 
  * along with this program; if not, write to the Free Software 
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0bea7f63
  *
99960be1
  * Exception: permission to copy, modify, propagate, and distribute a work
  * formed by combining OpenSSL toolkit software and the code in this file,
  * such as linking with software components and libraries released under
  * OpenSSL project license.
  *
0bea7f63
  */
 #include <openssl/hmac.h>
6a92939e
 #include <openssl/sha.h>
0bea7f63
 
cf83221d
 #include "../../core/basex.h"
 #include "../../core/dprint.h"
 #include "../../core/mod_fix.h"
 #include "../../core/str.h"
 #include "../../core/ut.h"
 #include "../../core/parser/digest/digest.h"
 #include "../../core/parser/hf.h"
 #include "../../core/mod_fix.h"
0bea7f63
 
3f3560c2
 #include "auth_ephemeral_mod.h"
0bea7f63
 #include "authorize.h"
 
e45df83d
 static inline int get_pass(str *_username, str *_secret, str *_password)
0bea7f63
 {
 	unsigned int hmac_len = SHA_DIGEST_LENGTH;
ced9bfbd
 	unsigned char hmac_sha1[512];
0bea7f63
 
765562b2
 	switch(autheph_sha_alg) {
 		case AUTHEPH_SHA1:
ced9bfbd
 			hmac_len = SHA_DIGEST_LENGTH;
765562b2
 			if (HMAC(EVP_sha1(), _secret->s, _secret->len,
 					(unsigned char *) _username->s,
 					_username->len, hmac_sha1, &hmac_len) == NULL)
 			{
 				LM_ERR("HMAC-SHA1 failed\n");
 				return -1;
 			}
 			break;
 		case AUTHEPH_SHA256:
ced9bfbd
 			hmac_len = SHA256_DIGEST_LENGTH;
 			if (HMAC(EVP_sha256(), _secret->s, _secret->len,
 					(unsigned char *) _username->s,
 					_username->len, hmac_sha1, &hmac_len) == NULL)
 			{
 				LM_ERR("HMAC-SHA256 failed\n");
 				return -1;
 			}
 			break;
 		case AUTHEPH_SHA384:
 			hmac_len = SHA384_DIGEST_LENGTH;
765562b2
 			if (HMAC(EVP_sha256(), _secret->s, _secret->len,
 					(unsigned char *) _username->s,
 					_username->len, hmac_sha1, &hmac_len) == NULL)
 			{
 				LM_ERR("HMAC-SHA256 failed\n");
 				return -1;
 			}
 			break;
 		case AUTHEPH_SHA512:
ced9bfbd
 			hmac_len = SHA512_DIGEST_LENGTH;
765562b2
 			if (HMAC(EVP_sha512(), _secret->s, _secret->len,
 					(unsigned char *) _username->s,
 					_username->len, hmac_sha1, &hmac_len) == NULL)
 			{
 				LM_ERR("HMAC-SHA512 failed\n");
 				return -1;
 			}
 			break;
 		default:
 			LM_ERR("Inavlid SHA Algorithm\n");
 			return -1;
 
0bea7f63
 	}
 
ced9bfbd
 	LM_DBG("HMAC-Len (%i)\n", hmac_len);
 
 
e45df83d
 	_password->len = base64_enc(hmac_sha1, hmac_len,
 					(unsigned char *) _password->s,
0bea7f63
 					base64_enc_len(hmac_len));
ced9bfbd
 	LM_DBG("calculated password: %.*s (%i)\n", _password->len, _password->s, _password->len);
e45df83d
 
 	return 0;
 }
 
 static inline int get_ha1(struct username *_username, str *_domain,
 				str *_secret, char *_ha1)
 {
ced9bfbd
 	char password[base64_enc_len(SHA512_DIGEST_LENGTH)];
e45df83d
 	str spassword;
 
0bea7f63
 	spassword.s = (char *) password;
e45df83d
 	spassword.len = 0;
 
 	if (get_pass(&_username->whole, _secret, &spassword) < 0)
 	{
 		LM_ERR("calculating password\n");
 		return -1;
 	}
0bea7f63
 
 	eph_auth_api.calc_HA1(HA_MD5, &_username->whole, _domain, &spassword,
 				0, 0, _ha1);
ba2a6ac4
 	LM_DBG("calculated HA1: %s\n", _ha1);
0bea7f63
 
 	return 0;
 }
 
e45df83d
 static inline int do_auth(struct sip_msg *_m, struct hdr_field *_h, str *_realm,
 			str *_method, str *_secret)
0bea7f63
 {
5937adf0
 	auth_result_t ret;
ced9bfbd
 	char ha1[512];
e45df83d
 	auth_body_t *cred = (auth_body_t*) _h->parsed;
0bea7f63
 
ced9bfbd
 	LM_DBG("secret: %.*s (%i)\n", _secret->len, _secret->s, _secret->len);
ba2a6ac4
 
e45df83d
 	if (get_ha1(&cred->digest.username, _realm, _secret, ha1) < 0)
0bea7f63
 	{
e45df83d
 		LM_ERR("calculating HA1\n");
0bea7f63
 		return AUTH_ERROR;
 	}
 
ced9bfbd
 	LM_DBG("HA1: %i\n", (int)strlen(ha1));
 	
e45df83d
 	ret = eph_auth_api.check_response(&cred->digest, _method, ha1);
0bea7f63
 	if (ret == AUTHENTICATED)
 	{
5937adf0
 		if (eph_auth_api.post_auth(_m, _h, ha1) != AUTHENTICATED) {
0bea7f63
 			return AUTH_ERROR;
 		}
5937adf0
 		return AUTH_OK;
 	} else if (ret == NOT_AUTHENTICATED) {
0bea7f63
 		return AUTH_INVALID_PASSWORD;
5937adf0
 	} else {
 		return AUTH_ERROR;
0bea7f63
 	}
 }
 
e45df83d
 int autheph_verify_timestamp(str *_username)
0bea7f63
 {
ba2a6ac4
 	int pos = 0, cur_time = (int) time(NULL);
0bea7f63
 	unsigned int expires;
 	str time_str = {0, 0};
 
e45df83d
 	while (pos < _username->len && _username->s[pos] != ':')
0bea7f63
 		pos++;
 
e45df83d
 	if (autheph_username_format == AUTHEPH_USERNAME_NON_IETF)
0bea7f63
 	{
e45df83d
 		if (pos < _username->len - 1)
 		{
 			time_str.s = _username->s + pos + 1;
 			time_str.len = _username->len - pos - 1;
 		}
 		else
 		{
 			time_str.s = _username->s;
 			time_str.len = _username->len;
 		}
0bea7f63
 	}
 	else
 	{
e45df83d
 		time_str.s = _username->s;
 		if (pos < _username->len - 1)
 		{
 			time_str.len = pos;
 		}
 		else
 		{
 			time_str.len = _username->len;
 		}
0bea7f63
 	}
 
ba2a6ac4
 	LM_DBG("username timestamp: %.*s\n", time_str.len, time_str.s);
0bea7f63
 	if (str2int(&time_str, &expires) < 0)
 	{
 		LM_ERR("unable to convert timestamp to int\n");
 		return -1;
 	}
 
ba2a6ac4
 	LM_DBG("current time: %d\n", cur_time);
 	if (cur_time > expires)
0bea7f63
 	{
 		LM_WARN("username has expired\n");
d354446a
 		return AUTH_USERNAME_EXPIRED;
0bea7f63
 	}
 
 	return 0;
 }
 
e45df83d
 static inline int digest_authenticate(struct sip_msg *_m, str *_realm,
 				hdr_types_t _hftype, str *_method)
0bea7f63
 {
 	struct hdr_field* h;
236b0b4b
 	auth_cfg_result_t ret = AUTH_ERROR;
5937adf0
 	auth_result_t rauth;
fd9fcd75
 	struct secret *secret_struct;
ba2a6ac4
 	str username;
 
e45df83d
 	LM_DBG("realm: %.*s\n", _realm->len, _realm->s);
 	LM_DBG("method: %.*s\n", _method->len, _method->s);
 
5937adf0
 	rauth = eph_auth_api.pre_auth(_m, _realm, _hftype, &h, NULL);
 	switch(rauth)
e45df83d
 	{
 	case NONCE_REUSED:
 		LM_DBG("nonce reused\n");
 		return AUTH_NONCE_REUSED;
 	case STALE_NONCE:
 		LM_DBG("stale nonce\n");
 		return AUTH_STALE_NONCE;
 	case NO_CREDENTIALS:
 		LM_DBG("no credentials\n");
 		return AUTH_NO_CREDENTIALS;
 	case ERROR:
 	case BAD_CREDENTIALS:
 		LM_DBG("error or bad credentials\n");
 		return AUTH_ERROR;
 	case CREATE_CHALLENGE:
 		LM_ERR("CREATE_CHALLENGE is not a valid state\n");
 		return AUTH_ERROR;
 	case DO_RESYNCHRONIZATION:
 		LM_ERR("DO_RESYNCHRONIZATION is not a valid state\n");
 		return AUTH_ERROR;
 	case NOT_AUTHENTICATED:
 		LM_DBG("not authenticated\n");
 		return AUTH_ERROR;
 	case DO_AUTHENTICATION:
 		break;
 	case AUTHENTICATED:
 		return AUTH_OK;
0bea7f63
 	}
 
ba2a6ac4
 	username = ((auth_body_t *) h->parsed)->digest.username.whole;
 	LM_DBG("username: %.*s\n", username.len, username.s);
 
d354446a
 	int res = autheph_verify_timestamp(&username);
 	if (res < 0)
0bea7f63
 	{
d354446a
 		if (res == -1)
 		{
 			LM_ERR("invalid timestamp in username\n");
 			return AUTH_ERROR;
 		} else {
 			return AUTH_USERNAME_EXPIRED;
 		}
0bea7f63
 	}
 
fd9fcd75
 	SECRET_LOCK;
 	secret_struct = secret_list;
0bea7f63
 	while (secret_struct != NULL)
 	{
e45df83d
 		ret = do_auth(_m, h, _realm, _method,
0bea7f63
 				&secret_struct->secret_key);
 		if (ret == AUTH_OK)
 		{
 			break;
 		}
 		secret_struct = secret_struct->next;
 	}
fd9fcd75
 	SECRET_UNLOCK;
0bea7f63
 
 	return ret;
 }
 
4861c7e8
 int ki_autheph_check(sip_msg_t *_m, str *srealm)
0bea7f63
 {
 
e45df83d
 	if (eph_auth_api.pre_auth == NULL)
 	{
 		LM_ERR("autheph_check() cannot be used without the auth "
 			"module\n");
 		return AUTH_ERROR;
 	}
 
5937adf0
 	if (_m->REQ_METHOD == METHOD_ACK || _m->REQ_METHOD == METHOD_CANCEL)
 	{
 		return AUTH_OK;
 	}
 
4861c7e8
 	if (srealm->len == 0)
0bea7f63
 	{
 		LM_ERR("invalid realm parameter - empty value\n");
 		return AUTH_ERROR;
 	}
 
e45df83d
 	if (_m->REQ_METHOD == METHOD_REGISTER)
 	{
4861c7e8
 		return digest_authenticate(_m, srealm, HDR_AUTHORIZATION_T,
0bea7f63
 					&_m->first_line.u.request.method);
e45df83d
 	}
0bea7f63
 	else
e45df83d
 	{
4861c7e8
 		return digest_authenticate(_m, srealm, HDR_PROXYAUTH_T,
0bea7f63
 					&_m->first_line.u.request.method);
e45df83d
 	}
0bea7f63
 }
 
4861c7e8
 int autheph_check(struct sip_msg *_m, char *_realm, char *_p2)
0bea7f63
 {
 	str srealm;
 
4861c7e8
 	if(_m == NULL || _realm == NULL)
e45df83d
 	{
4861c7e8
 		LM_ERR("invalid parameters\n");
e45df83d
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	if (get_str_fparam(&srealm, _m, (fparam_t*)_realm) < 0)
0bea7f63
 	{
4861c7e8
 		LM_ERR("failed to get realm value\n");
0bea7f63
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	return ki_autheph_check(_m, &srealm);
 }
 
 int ki_autheph_www(sip_msg_t *_m, str *srealm)
 {
 	if (eph_auth_api.pre_auth == NULL)
5937adf0
 	{
4861c7e8
 		LM_ERR("autheph_www() cannot be used without the auth "
 			"module\n");
 		return AUTH_ERROR;
5937adf0
 	}
 
4861c7e8
 	if (_m->REQ_METHOD == METHOD_ACK || _m->REQ_METHOD == METHOD_CANCEL)
0bea7f63
 	{
4861c7e8
 		return AUTH_OK;
0bea7f63
 	}
 
4861c7e8
 	if (srealm->len == 0)
0bea7f63
 	{
 		LM_ERR("invalid realm parameter - empty value\n");
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	return digest_authenticate(_m, srealm, HDR_AUTHORIZATION_T,
0bea7f63
 					&_m->first_line.u.request.method);
 }
 
4861c7e8
 int autheph_www(struct sip_msg *_m, char *_realm, char *_p2)
0bea7f63
 {
 	str srealm;
e45df83d
 
 	if(_m == NULL || _realm == NULL)
0bea7f63
 	{
 		LM_ERR("invalid parameters\n");
 		return AUTH_ERROR;
 	}
 
 	if (get_str_fparam(&srealm, _m, (fparam_t*)_realm) < 0)
 	{
 		LM_ERR("failed to get realm value\n");
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	return ki_autheph_www(_m, &srealm);
 }
 
 int ki_autheph_www_method(sip_msg_t *_m, str *srealm, str *smethod)
 {
 	if (eph_auth_api.pre_auth == NULL)
0bea7f63
 	{
4861c7e8
 		LM_ERR("autheph_www() cannot be used without the auth "
 			"module\n");
0bea7f63
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	if (_m->REQ_METHOD == METHOD_ACK || _m->REQ_METHOD == METHOD_CANCEL)
0bea7f63
 	{
4861c7e8
 		return AUTH_OK;
 	}
 
 	if (srealm->len == 0)
 	{
 		LM_ERR("invalid realm parameter - empty value\n");
0bea7f63
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	if (smethod->len == 0)
0bea7f63
 	{
 		LM_ERR("invalid method value - empty value\n");
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	return digest_authenticate(_m, srealm, HDR_AUTHORIZATION_T, smethod);
0bea7f63
 }
 
4861c7e8
 int autheph_www2(struct sip_msg *_m, char *_realm, char *_method)
0bea7f63
 {
 	str srealm;
4861c7e8
 	str smethod;
0bea7f63
 
4861c7e8
 	if(_m == NULL || _realm == NULL || _method == NULL)
e45df83d
 	{
4861c7e8
 		LM_ERR("invalid parameters\n");
e45df83d
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	if (get_str_fparam(&srealm, _m, (fparam_t*)_realm) < 0)
0bea7f63
 	{
4861c7e8
 		LM_ERR("failed to get realm value\n");
0bea7f63
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	if (get_str_fparam(&smethod, _m, (fparam_t*)_method) < 0)
5937adf0
 	{
4861c7e8
 		LM_ERR("failed to get method value\n");
 		return AUTH_ERROR;
5937adf0
 	}
 
4861c7e8
 	return ki_autheph_www_method(_m, &srealm, &smethod);
 }
 
 int ki_autheph_proxy(sip_msg_t *_m, str *srealm)
 {
 	if (eph_auth_api.pre_auth == NULL)
0bea7f63
 	{
4861c7e8
 		LM_ERR("autheph_proxy() cannot be used without the auth "
 			"module\n");
0bea7f63
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	if (_m->REQ_METHOD == METHOD_ACK || _m->REQ_METHOD == METHOD_CANCEL)
 	{
 		return AUTH_OK;
 	}
 
 	if (srealm->len == 0)
0bea7f63
 	{
 		LM_ERR("invalid realm parameter - empty value\n");
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	return digest_authenticate(_m, srealm, HDR_PROXYAUTH_T,
0bea7f63
 					&_m->first_line.u.request.method);
 }
e45df83d
 
4861c7e8
 int autheph_proxy(struct sip_msg *_m, char *_realm, char *_p2)
e45df83d
 {
4861c7e8
 	str srealm;
e45df83d
 
4861c7e8
 	if(_m == NULL || _realm == NULL)
e45df83d
 	{
 		LM_ERR("invalid parameters\n");
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	if (get_str_fparam(&srealm, _m, (fparam_t*)_realm) < 0)
e45df83d
 	{
4861c7e8
 		LM_ERR("failed to get realm value\n");
e45df83d
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	return ki_autheph_proxy(_m, &srealm);
 }
e45df83d
 
4861c7e8
 int ki_autheph_authenticate(sip_msg_t *_m, str *susername, str *spassword)
 {
 	char generated_password[base64_enc_len(SHA_DIGEST_LENGTH)];
 	str sgenerated_password;
 	struct secret *secret_struct;
 
 	if (susername->len == 0)
e45df83d
 	{
4861c7e8
 		LM_ERR("invalid username parameter - empty value\n");
e45df83d
 		return AUTH_ERROR;
 	}
 
4861c7e8
 	if (spassword->len == 0)
e45df83d
 	{
 		LM_ERR("invalid password parameter - empty value\n");
 		return AUTH_ERROR;
 	}
 
d354446a
 	int res = autheph_verify_timestamp(susername);
 	if (res < 0)
e45df83d
 	{
d354446a
 		if (res == -1)
 		{
 			LM_ERR("invalid timestamp in username\n");
 			return AUTH_ERROR;
 		} else {
 			return AUTH_USERNAME_EXPIRED;
 		}
e45df83d
 	}
 
4861c7e8
 	LM_DBG("username: %.*s\n", susername->len, susername->s);
 	LM_DBG("password: %.*s\n", spassword->len, spassword->s);
e45df83d
 
 	sgenerated_password.s = generated_password;
fd9fcd75
 	SECRET_LOCK;
 	secret_struct = secret_list;
e45df83d
 	while (secret_struct != NULL)
 	{
 		LM_DBG("trying secret: %.*s\n",
 			secret_struct->secret_key.len,
 			secret_struct->secret_key.s);
4861c7e8
 		if (get_pass(susername, &secret_struct->secret_key,
e45df83d
 				&sgenerated_password) == 0)
 		{
 			LM_DBG("generated password: %.*s\n",
 				sgenerated_password.len, sgenerated_password.s);
42129d35
 			if (spassword->len == sgenerated_password.len
 					&& strncmp(spassword->s, sgenerated_password.s,
 						spassword->len) == 0)
e45df83d
 			{
fd9fcd75
 				SECRET_UNLOCK;
e45df83d
 				return AUTH_OK;
 			}
 		}
 		secret_struct = secret_struct->next;
 	}
fd9fcd75
 	SECRET_UNLOCK;
e45df83d
 
 	return AUTH_ERROR;
 }
4861c7e8
 
 int autheph_authenticate(struct sip_msg *_m, char *_username, char *_password)
 {
 	str susername, spassword;
 
 	if (_m == NULL || _username == NULL || _password == NULL)
 	{
 		LM_ERR("invalid parameters\n");
 		return AUTH_ERROR;
 	}
 
 	if (get_str_fparam(&susername, _m, (fparam_t*)_username) < 0)
 	{
 		LM_ERR("failed to get username value\n");
 		return AUTH_ERROR;
 	}
 
 	if (get_str_fparam(&spassword, _m, (fparam_t*)_password) < 0)
 	{
 		LM_ERR("failed to get password value\n");
 		return AUTH_ERROR;
 	}
 
 	return ki_autheph_authenticate(_m, &susername, &spassword);
236b0b4b
 }