Commit 3ae4dd71 authored by Simon Morlat's avatar Simon Morlat
Browse files

Fix various things around authentication with SHA-256. Especially it allows to...

Fix various things around authentication with SHA-256. Especially it allows to authenticate with SHA256 by supplier an auth info with a clear text password, and algorithm left unspecified.
parent 5a0e2416
......@@ -78,7 +78,25 @@ static bool_t realm_match(const char *realm1, const char *realm2){
return FALSE;
}
static const LinphoneAuthInfo *find_auth_info(LinphoneCore *lc, const char *username, const char *realm, const char *domain, bool_t ignore_realm){
/* Check if the LinphoneAuthInfo candidate is compatible with the requested algorithm. */
static bool_t check_algorithm_compatibility(const LinphoneAuthInfo *ai, const char *algorithm){
const char *ai_algorithm = linphone_auth_info_get_algorithm(ai);
if (algorithm == NULL) return TRUE;
if (linphone_auth_info_get_password(ai) != NULL){
/* We have the clear text password, so if the user didn't requested a specific algorithm, we can satisfy all algorithms.*/
if (ai_algorithm == NULL) return TRUE;
}else{
/* If we don't have the clear text password but the ha1, and if algorithm is empty in LinphoneAuthInfo
* for backward compatibility, we assume it is MD5. */
if (ai_algorithm == NULL && strcasecmp(algorithm, "MD5") == 0) return TRUE;
}
/* In all other cases, algorithm must match. */
if (ai_algorithm && strcasecmp(algorithm, ai_algorithm) == 0) return TRUE; /* algorithm do match */
return FALSE;
}
static const LinphoneAuthInfo *find_auth_info(LinphoneCore *lc, const char *username, const char *realm, const char *domain, const char *algorithm, bool_t ignore_realm){
bctbx_list_t *elem;
const LinphoneAuthInfo *ret=NULL;
......@@ -87,6 +105,10 @@ static const LinphoneAuthInfo *find_auth_info(LinphoneCore *lc, const char *user
if (username && linphone_auth_info_get_username(pinfo) && strcmp(username, linphone_auth_info_get_username(pinfo))==0)
{
if (!check_algorithm_compatibility(pinfo, algorithm)) {
continue;
}
if (realm && domain){
if (linphone_auth_info_get_realm(pinfo) && realm_match(realm, linphone_auth_info_get_realm(pinfo))
&& linphone_auth_info_get_domain(pinfo) && strcmp(domain, linphone_auth_info_get_domain(pinfo))==0) {
......@@ -123,19 +145,19 @@ const LinphoneAuthInfo *_linphone_core_find_tls_auth_info(LinphoneCore *lc) {
return NULL;
}
const LinphoneAuthInfo *_linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain, bool_t ignore_realm){
const LinphoneAuthInfo *_linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain, const char *algorithm, bool_t ignore_realm){
const LinphoneAuthInfo *ai=NULL;
if (realm){
ai=find_auth_info(lc,username,realm,NULL, FALSE);
ai=find_auth_info(lc,username,realm,NULL, algorithm, FALSE);
if (ai==NULL && domain){
ai=find_auth_info(lc,username,realm,domain, FALSE);
ai=find_auth_info(lc,username,realm,domain, algorithm, FALSE);
}
}
if (ai == NULL && domain != NULL) {
ai=find_auth_info(lc,username,NULL,domain, ignore_realm);
ai=find_auth_info(lc,username,NULL,domain, algorithm, ignore_realm);
}
if (ai==NULL){
ai=find_auth_info(lc,username,NULL,NULL, ignore_realm);
ai=find_auth_info(lc,username,NULL,NULL, algorithm, ignore_realm);
}
if (ai) ms_message("linphone_core_find_auth_info(): returning auth info username=%s, realm=%s", linphone_auth_info_get_username(ai) ? linphone_auth_info_get_username(ai) : "", linphone_auth_info_get_realm(ai) ? linphone_auth_info_get_realm(ai) : "");
......@@ -143,7 +165,7 @@ const LinphoneAuthInfo *_linphone_core_find_auth_info(LinphoneCore *lc, const ch
}
const LinphoneAuthInfo *linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain){
return _linphone_core_find_auth_info(lc, realm, username, domain, TRUE);
return _linphone_core_find_auth_info(lc, realm, username, domain, NULL, TRUE);
}
/*the auth info is expected to be in the core's list*/
......@@ -200,7 +222,7 @@ void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info)
for (const auto &op : pendingAuths) {
LinphoneAuthInfo *ai;
const SalAuthInfo *req_sai=op->getAuthRequested();
ai=(LinphoneAuthInfo*)_linphone_core_find_auth_info(lc, req_sai->realm, req_sai->username, req_sai->domain, FALSE);
ai=(LinphoneAuthInfo*)_linphone_core_find_auth_info(lc, req_sai->realm, req_sai->username, req_sai->domain, req_sai->algorithm, FALSE);
if (ai){
SalAuthInfo sai;
bctbx_list_t* proxy;
......
......@@ -363,7 +363,7 @@ static void auth_failure(SalOp *op, SalAuthInfo* info) {
LinphoneAuthInfo *ai = NULL;
if (info != NULL) {
ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc, info->realm, info->username, info->domain, TRUE);
ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc, info->realm, info->username, info->domain, info->algorithm, TRUE);
if (ai){
LinphoneAuthMethod method = info->mode == SalAuthModeHttpDigest ? LinphoneAuthHttpDigest : LinphoneAuthTls;
LinphoneAuthInfo *auth_info = linphone_core_create_auth_info(lc, info->username, NULL, NULL, NULL, info->realm, info->domain);
......@@ -545,23 +545,10 @@ static bool_t fill_auth_info(LinphoneCore *lc, SalAuthInfo* sai) {
if (sai->mode == SalAuthModeTls) {
ai = (LinphoneAuthInfo*)_linphone_core_find_tls_auth_info(lc);
} else {
ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc,sai->realm,sai->username,sai->domain, FALSE);
ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc,sai->realm,sai->username,sai->domain, sai->algorithm, FALSE);
}
if (ai) {
if (sai->mode == SalAuthModeHttpDigest) {
/*
* Compare algorithm of server(sai) with algorithm of client(ai), if they are not correspondant,
* exit. The default algorithm is MD5 if it's NULL.
*/
if (sai->algorithm && linphone_auth_info_get_algorithm(ai)) {
if (strcasecmp(linphone_auth_info_get_algorithm(ai), sai->algorithm))
return TRUE;
} else if (
(linphone_auth_info_get_algorithm(ai) && strcasecmp(linphone_auth_info_get_algorithm(ai), "MD5")) ||
(sai->algorithm && strcasecmp(sai->algorithm, "MD5"))
)
return TRUE;
sai->userid = ms_strdup(linphone_auth_info_get_userid(ai) ? linphone_auth_info_get_userid(ai) : linphone_auth_info_get_username(ai));
sai->password = linphone_auth_info_get_passwd(ai)?ms_strdup(linphone_auth_info_get_passwd(ai)) : NULL;
sai->ha1 = linphone_auth_info_get_ha1(ai) ? ms_strdup(linphone_auth_info_get_ha1(ai)) : NULL;
......@@ -577,9 +564,10 @@ static bool_t fill_auth_info(LinphoneCore *lc, SalAuthInfo* sai) {
}
}
if (sai->realm && !linphone_auth_info_get_realm(ai)){
if (sai->realm && (!linphone_auth_info_get_realm(ai) || !linphone_auth_info_get_algorithm(ai))) {
/*if realm was not known, then set it so that ha1 may eventually be calculated and clear text password dropped*/
linphone_auth_info_set_realm(ai, sai->realm);
linphone_auth_info_set_algorithm(ai, sai->algorithm);
linphone_core_write_auth_info(lc, ai);
}
return TRUE;
......
......@@ -126,7 +126,7 @@ LINPHONE_PUBLIC void linphone_call_params_set_no_user_consent(LinphoneCallParams
void _linphone_core_uninit(LinphoneCore *lc);
void linphone_core_write_auth_info(LinphoneCore *lc, LinphoneAuthInfo *ai);
const LinphoneAuthInfo *_linphone_core_find_tls_auth_info(LinphoneCore *lc);
const LinphoneAuthInfo *_linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain, bool_t ignore_realm);
const LinphoneAuthInfo *_linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain, const char *algorithm, bool_t ignore_realm);
void linphone_auth_info_fill_belle_sip_event(const LinphoneAuthInfo *auth_info, belle_sip_auth_event *event);
void linphone_core_update_proxy_register(LinphoneCore *lc);
......
......@@ -1729,7 +1729,7 @@ const LinphoneAddress *_linphone_proxy_config_get_contact_without_params (const
const struct _LinphoneAuthInfo* linphone_proxy_config_find_auth_info(const LinphoneProxyConfig *cfg) {
const char* username = cfg->identity_address ? linphone_address_get_username(cfg->identity_address) : NULL;
const char* domain = cfg->identity_address ? linphone_address_get_domain(cfg->identity_address) : NULL;
return _linphone_core_find_auth_info(cfg->lc, cfg->realm, username, domain, TRUE);
return linphone_core_find_auth_info(cfg->lc, cfg->realm, username, domain);
}
const char * linphone_proxy_config_get_ref_key(const LinphoneProxyConfig *cfg) {
......
......@@ -27,76 +27,73 @@ using namespace std;
LINPHONE_BEGIN_NAMESPACE
AuthInfo::AuthInfo(const string &username, const string &userid, const string &passwd, const string &ha1, const string &realm, const string &domain){
AuthInfo::init(username, userid, passwd, ha1, realm, domain, "");
AuthInfo::init(username, userid, passwd, ha1, realm, domain, "");
}
AuthInfo::AuthInfo(const string &username, const string &userid, const string &passwd, const string &ha1, const string &realm, const string &domain, const string &algorithm){
AuthInfo::init(username, userid, passwd, ha1, realm, domain, algorithm);
if(algorithm.empty()){
mAlgorithm = "MD5";
}
else{
mAlgorithm = algorithm;
}
AuthInfo::init(username, userid, passwd, ha1, realm, domain, algorithm);
}
void AuthInfo::init(const string &username, const string &userid, const string &passwd, const string &ha1, const string &realm, const string &domain, const string &algorithm){
mUsername = username;
mUserid = userid;
mPasswd = passwd;
mHa1 = ha1;
mRealm = realm;
mDomain = domain;
mAlgorithm = algorithm;
mUsername = username;
mUserid = userid;
mPasswd = passwd;
mHa1 = ha1;
mRealm = realm;
mDomain = domain;
mAlgorithm = algorithm;
if (!ha1.empty() && algorithm.empty()) {
/* When ha1 is specified, algorithm must be specified too. For backward compatibility when this is not the case, we assume it is md5.*/
mAlgorithm = "MD5";
}
}
AuthInfo::AuthInfo(LpConfig *config, string key){
const char *username, *userid, *passwd, *ha1, *realm, *domain, *tls_cert_path, *tls_key_path, *algo;
const char *username, *userid, *passwd, *ha1, *realm, *domain, *tls_cert_path, *tls_key_path, *algo;
username = lp_config_get_string(config, key.c_str(), "username", "");
username = lp_config_get_string(config, key.c_str(), "username", "");
userid = lp_config_get_string(config, key.c_str(), "userid", "");
passwd = lp_config_get_string(config, key.c_str(), "passwd", "");
ha1 = lp_config_get_string(config, key.c_str(), "ha1", "");
realm = lp_config_get_string(config, key.c_str(), "realm", "");
domain = lp_config_get_string(config, key.c_str(), "domain", "");
tls_cert_path = lp_config_get_string(config, key.c_str(), "client_cert_chain", "");
tls_key_path = lp_config_get_string(config, key.c_str(), "client_cert_key", "");
algo = lp_config_get_string(config, key.c_str(), "algorithm", "MD5");
tls_key_path = lp_config_get_string(config, key.c_str(), "client_cert_key", "");
algo = lp_config_get_string(config, key.c_str(), "algorithm", "MD5");
setTlsCertPath(tls_cert_path);
setTlsKeyPath(tls_key_path);
setTlsCertPath(tls_cert_path);
setTlsKeyPath(tls_key_path);
init(username, userid, passwd, ha1, realm, domain, algo);
init(username, userid, passwd, ha1, realm, domain, algo);
}
AuthInfo* AuthInfo::clone() const {
AuthInfo *ai = new AuthInfo(getUsername(), getUserid(), getPassword(), getHa1(), getRealm(), getDomain(), getAlgorithm());
ai->setTlsCert(getTlsCert());
ai->setTlsCertPath(getTlsCertPath());
ai->setTlsKey(getTlsKey());
ai->setTlsKeyPath(getTlsKeyPath());
return ai;
AuthInfo *ai = new AuthInfo(getUsername(), getUserid(), getPassword(), getHa1(), getRealm(), getDomain(), getAlgorithm());
ai->setTlsCert(getTlsCert());
ai->setTlsCertPath(getTlsCertPath());
ai->setTlsKey(getTlsKey());
ai->setTlsKeyPath(getTlsKeyPath());
return ai;
}
const string& AuthInfo::getUsername() const{
return mUsername;
return mUsername;
}
const string& AuthInfo::getAlgorithm() const{
return mAlgorithm;
return mAlgorithm;
}
const string& AuthInfo::getPassword() const{
return mPasswd;
return mPasswd;
}
const string& AuthInfo::getUserid() const{
return mUserid;
return mUserid;
}
const string& AuthInfo::getRealm() const{
return mRealm;
return mRealm;
}
const string& AuthInfo::getDomain() const{
......@@ -132,16 +129,10 @@ void AuthInfo::setUsername(const string &username){
}
void AuthInfo::setAlgorithm(const string &algorithm){
if(!algorithm.empty()){
mAlgorithm = algorithm;
}
if(algorithm.compare("MD5") == 0 && algorithm.compare("SHA-256") == 0){
lError() << "Given algorithm is not correct. Set algorithm failed";
}
else{
mAlgorithm = "MD5";
}
if (!algorithm.empty() && algorithm != "MD5" && algorithm != "SHA-256"){
lError() << "Given algorithm is not correct. Set algorithm failed";
}
mAlgorithm = algorithm;
}
void AuthInfo::setUserid(const string &userid){
......@@ -177,46 +168,42 @@ void AuthInfo::setTlsKeyPath(const string &tlsKeyPath){
}
void AuthInfo::writeConfig(LpConfig *config, int pos){
char key[50];
char *myHa1;
char key[50];
char *myHa1;
bool_t store_ha1_passwd = !!lp_config_get_int(config, "sip", "store_ha1_passwd", 1);
sprintf(key, "auth_info_%i", pos);
lp_config_clean_section(config, key);
if (lp_config_get_int(config, "sip", "store_auth_info", 1) == 0) {
if (lp_config_get_int(config, "sip", "store_auth_info", 1) == 0) {
return;
}
if (getHa1().empty() && !getRealm().empty() && !getPassword().empty() && (!getUsername().empty() || !getUserid().empty()) && store_ha1_passwd) {
if (getHa1().empty() && !getRealm().empty() && !getPassword().empty() && (!getUsername().empty() || !getUserid().empty()) && store_ha1_passwd) {
/* Default algorithm is MD5 if it's NULL */
if(getAlgorithm().empty() || getAlgorithm().compare("MD5") == 0){
myHa1 = reinterpret_cast<char *>(ms_malloc(33));
sal_auth_compute_ha1(getUserid().empty() ? getUsername().c_str() : getUserid().c_str(), getRealm().c_str(), getPassword().c_str(), myHa1);
mHa1 = myHa1;
ms_free(myHa1);
}
/* If algorithm is SHA-256, calcul ha1 by sha256*/
else if(getAlgorithm().compare("SHA-256") == 0){
myHa1 = reinterpret_cast<char *>(ms_malloc(65));
sal_auth_compute_ha1_for_algorithm(getUserid().empty() ? getUsername().c_str() : getUserid().c_str(), getRealm().c_str(), getPassword().c_str(), myHa1, 65, getAlgorithm().c_str());
mHa1 = myHa1;
ms_free(myHa1);
}
}
if(getAlgorithm().empty() || getAlgorithm().compare("MD5") == 0){
myHa1 = reinterpret_cast<char *>(ms_malloc(33));
sal_auth_compute_ha1(getUserid().empty() ? getUsername().c_str() : getUserid().c_str(), getRealm().c_str(), getPassword().c_str(), myHa1);
mHa1 = myHa1;
ms_free(myHa1);
}
/* If algorithm is SHA-256, calcul ha1 by sha256*/
else if(getAlgorithm().compare("SHA-256") == 0){
myHa1 = reinterpret_cast<char *>(ms_malloc(65));
sal_auth_compute_ha1_for_algorithm(getUserid().empty() ? getUsername().c_str() : getUserid().c_str(), getRealm().c_str(), getPassword().c_str(), myHa1, 65, getAlgorithm().c_str());
mHa1 = myHa1;
ms_free(myHa1);
}
}
lp_config_set_string(config, key, "username", getUsername().c_str());
lp_config_set_string(config, key, "userid", getUserid().c_str());
lp_config_set_string(config, key, "ha1", getHa1().c_str());
lp_config_set_string(config, key, "ha1", getHa1().c_str());
if (store_ha1_passwd && !getHa1().empty()) {
/*if we have our ha1 and store_ha1_passwd set to TRUE, then drop the clear text password for security*/
setPassword("");
}
else {
/*we store clear text password only if store_ha1_passwd is FALSE AND we have an ha1 to store. Otherwise, passwd would simply be removed, which might bring major auth issue*/
lp_config_set_string(config, key, "passwd", getPassword().c_str());
}
/*if we have our ha1 and store_ha1_passwd set to TRUE, then drop the clear text password for security*/
setPassword("");
}
lp_config_set_string(config, key, "passwd", getPassword().c_str());
lp_config_set_string(config, key, "realm", getRealm().c_str());
lp_config_set_string(config, key, "domain", getDomain().c_str());
lp_config_set_string(config, key, "client_cert_chain", getTlsCertPath().c_str());
......@@ -225,15 +212,15 @@ void AuthInfo::writeConfig(LpConfig *config, int pos){
}
std::string AuthInfo::toString() const{
std::ostringstream ss;
std::ostringstream ss;
ss << "Username[" << mUsername << "];";
ss << "Username[" << mUsername << "];";
ss << "Userid[" << mUserid << "];";
ss << "Realm[" << mRealm << "];";
ss << "Domain[" << mDomain << "];";
ss << "Algorithm[" << mAlgorithm << "];";
ss << "Realm[" << mRealm << "];";
ss << "Domain[" << mDomain << "];";
ss << "Algorithm[" << mAlgorithm << "];";
return ss.str();
}
LINPHONE_END_NAMESPACE
\ No newline at end of file
LINPHONE_END_NAMESPACE
......@@ -138,6 +138,7 @@ extern const char* test_domain;
extern const char* auth_domain;
extern const char* test_username;
extern const char* test_sha_username;
extern const char* pure_sha256_user;
extern const char* test_password;
extern const char* test_route;
extern const char* userhostsfile;
......
......@@ -345,6 +345,42 @@ static void simple_authenticated_register_for_algorithm(void){
linphone_core_manager_destroy(lcm);
}
static void simple_authenticated_register_with_SHA256_from_clear_text_password(void){
/*
* In this test, the user has only a SHA256 hashed password on the server.
* We want to check that it can register by providing the corresponding clear text password, without the knowledge of which
* algorithm is used for his account.
*/
stats* counters;
LinphoneCoreManager* lcm = create_lcm();
LinphoneAuthInfo *info=linphone_auth_info_new_for_algorithm(pure_sha256_user,NULL,test_password,NULL,NULL,auth_domain,NULL);
LinphoneConfig *lcfg;
const LinphoneAuthInfo *new_info;
char route[256];
const char *ha1;
const char *cfg_ha1;
sprintf(route,"sip:%s",test_route);
linphone_core_add_auth_info(lcm->lc,info); /*add authentication info to LinphoneCore*/
linphone_auth_info_unref(info);
counters = &lcm->stat;
register_with_refresh_base_for_algo(lcm->lc, FALSE, auth_domain, route, pure_sha256_user);
BC_ASSERT_EQUAL(counters->number_of_auth_info_requested,0, int, "%d");
/* Assert that the ha1 was correctly computed, and stored in configuration. */
new_info = linphone_core_find_auth_info(lcm->lc, NULL, pure_sha256_user, NULL);
BC_ASSERT_PTR_NULL(linphone_auth_info_get_password(new_info));
ha1 = linphone_auth_info_get_ha1(new_info);
BC_ASSERT_PTR_NOT_NULL(ha1);
lcfg = linphone_core_get_config(lcm->lc);
cfg_ha1 = linphone_config_get_string(lcfg, "auth_info_0", "ha1", "");
BC_ASSERT_STRING_EQUAL(cfg_ha1, ha1);
BC_ASSERT_STRING_EQUAL(linphone_auth_info_get_realm(new_info), auth_domain);
BC_ASSERT_STRING_EQUAL(linphone_config_get_string(lcfg, "auth_info_0", "algorithm", ""), "SHA-256");
BC_ASSERT_PTR_NULL(linphone_config_get_string(lcfg, "auth_info_0", "passwd", NULL));
linphone_core_manager_destroy(lcm);
}
static void ha1_authenticated_register(void){
stats* counters;
LinphoneCoreManager* lcm = create_lcm();
......@@ -1457,6 +1493,7 @@ test_t register_tests[] = {
TEST_NO_TAG("TLS with non tls server",tls_with_non_tls_server),
TEST_NO_TAG("Simple authenticated register", simple_authenticated_register),
TEST_NO_TAG("Simple authenticated register SHA-256", simple_authenticated_register_for_algorithm),
TEST_NO_TAG("Simple authenticated register SHA-256 from cleartext password", simple_authenticated_register_with_SHA256_from_clear_text_password),
TEST_NO_TAG("Ha1 authenticated register", ha1_authenticated_register),
TEST_NO_TAG("Ha1 authenticated register SHA-256", ha1_authenticated_register_for_algorithm),
TEST_NO_TAG("Digest auth without initial credentials", authenticated_register_with_no_initial_credentials),
......
......@@ -52,6 +52,7 @@ const char* test_domain="sipopen.example.org";
const char* auth_domain="sip.example.org";
const char* test_username="liblinphone_tester";
const char* test_sha_username="liblinphone_sha_tester";
const char* pure_sha256_user="pure_sha256_user";
const char* test_password="secret";
const char* test_route="sip2.linphone.org";
const char *userhostsfile = "tester_hosts";
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment