From 1af265f9d4cd84240ca745a0429e1bf5a30455ec Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 30 Oct 2020 12:09:09 +0100 Subject: [PATCH] Fixed digest auth algorithm issue (no algo was sent correctly during 401). Fixed double algorithm issue when account only has one algorithm (missing hyphen), added logs, fixed indent. --- flexisip-account-manager.spec | 2 +- src/xmlrpc/authentication.php | 73 ++++++++++++++++++++++++++++++++--- src/xmlrpc/provisioning.php | 15 +++++-- 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/flexisip-account-manager.spec b/flexisip-account-manager.spec index 99a2f24..ad8e76a 100644 --- a/flexisip-account-manager.spec +++ b/flexisip-account-manager.spec @@ -8,7 +8,7 @@ #%define _datadir %{_datarootdir} #%define _docdir %{_datadir}/doc -%define build_number 34 +%define build_number 36 %define var_dir /var/opt/belledonne-communications %define opt_dir /opt/belledonne-communications/share/flexisip-account-manager %define env_file "$RPM_BUILD_ROOT/etc/flexisip-account-manager/flexiapi.env" diff --git a/src/xmlrpc/authentication.php b/src/xmlrpc/authentication.php index 301e8ad..db6bc47 100644 --- a/src/xmlrpc/authentication.php +++ b/src/xmlrpc/authentication.php @@ -41,12 +41,73 @@ function auth_get_valid_nonces() hash_hmac("sha256", $time-MIN_NONCE_VALIDITY_PERIOD.':'.$request, AUTH_NONCE_KEY)); } -function request_authentication($realm = "sip.example.org") +function request_authentication($realm = "sip.example.org", $username = null) { - header('HTTP/1.1 401 Unauthorized'); - header('WWW-Authenticate: Digest realm="' . $realm. - '",qop="auth",nonce="' . auth_get_valid_nonces()[0] . '",opaque="' . md5($realm) . '"'); - exit(); + $has_md5 = false; + $has_sha256 = false; + + if ($username != null) { + // Get the password/hash from database to include only available password hash in the authenticate header + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $username; + + if (!$account->getOne()) { + Logger::getInstance()->error("Couldn't find account " . (string)$account); + return null; + } + $pwd = new Password($db); + $pwd->account_id = $account->id; + + $stmt = $pwd->getAll(); + $num = $stmt->rowCount(); + if ($num <= 0) { + Logger::getInstance()->error("Couldn't find password " . (string)$pwd); + return null; + } + + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + extract($row); + // Generate the valid response + switch ($algorithm) { + case 'CLRTXT': + $has_md5 = true; + $has_sha256 = true; + break; + case 'MD5': + $has_md5 = true; + break; + case 'SHA-256': + $has_sha256 = true; + break; + default: + Logger::getInstance()->error("Digest error : Given algorithm '" . $algorithm . "' is invalid (neither of 'CLRTXT', 'MD5', 'SHA-256')"); + } + } + } else { // we don't have the username, authorize both MD5 and SHA256 + $has_md5 = true; + $has_sha256 = true; + Logger::getInstance()->debug("Username not found, replying with both auth anyway"); + } + + if (($has_md5 || $has_sha256) == false) { + // reply anyway with both hash authorized + $has_md5 = true; + $has_sha256 = true; + Logger::getInstance()->debug("Doesn't have MD5 or SHA-256, replying with both auth anyway"); + } + + header('HTTP/1.1 401 Unauthorized'); + if ($has_md5 == true) { + header('WWW-Authenticate: Digest realm="' . $realm. + '",qop="auth",algorithm=MD5,nonce="' . auth_get_valid_nonces()[0] . '",opaque="' . md5($realm) . '"'); + } + if ($has_sha256 == true) { + header('WWW-Authenticate: Digest realm="' . $realm. + '",qop="auth",algorithm=SHA-256,nonce="' . auth_get_valid_nonces()[0] . '",opaque="' . md5($realm) . '"', false); + } + exit(); } function authenticate($auth_digest, $realm = "sip.example.org") @@ -96,6 +157,8 @@ function authenticate($auth_digest, $realm = "sip.example.org") $A2 = hash('sha256', getenv('REQUEST_METHOD').':'.$data['uri']); $valid_response = hash('sha256', $A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2); break; + default: + Logger::getInstance()->error("Digest error : Given algorithm '" . $algorithm . "' is invalid (neither of 'CLRTXT', 'MD5', 'SHA-256')"); } // Compare with the client response diff --git a/src/xmlrpc/provisioning.php b/src/xmlrpc/provisioning.php index 24b89ed..a5d9626 100644 --- a/src/xmlrpc/provisioning.php +++ b/src/xmlrpc/provisioning.php @@ -29,8 +29,18 @@ include_once __DIR__ . '/authentication.php'; $logger = Logger::getInstance(); +$username = isset($_GET['username']) ? $_GET['username'] : null; + if (REMOTE_PROVISIONING_USE_DIGEST_AUTH) { $headers = getallheaders(); + // From is the GRUU('sip:username@AUTH_REALM;gr=*;), we need to extract the username from it: + // from position 4(skip 'sip:') until the first occurence of @ + // pass it through rawurldecode has GRUU may contain escaped characters + $from = rawurldecode(substr($headers['From'],4,strpos($headers['From'], '@')-4)); + if (empty($from)) { + $from = $username; + $logger->debug("Empty From, using username = " . $username); + } $authorization = null; // Get authentication header if there is one @@ -49,11 +59,11 @@ if (REMOTE_PROVISIONING_USE_DIGEST_AUTH) { Logger::getInstance()->debug("Authentication successful"); } else { Logger::getInstance()->debug("Authentication failed"); - request_authentication(AUTH_REALM); + request_authentication(AUTH_REALM, $from); } } else { Logger::getInstance()->debug("No authentication header"); - request_authentication(AUTH_REALM); + request_authentication(AUTH_REALM, $from); } } @@ -109,7 +119,6 @@ if (file_exists(REMOTE_PROVISIONING_DEFAULT_CONFIG)) { } } -$username = isset($_GET['username']) ? $_GET['username'] : null; $domain = isset($_GET['domain']) ? $_GET['domain'] : SIP_DOMAIN; $transport = isset($_GET['transport']) ? $_GET['transport'] : REMOTE_PROVISIONING_DEFAULT_TRANSPORT;