From a30b4662b4ff2cfe19c93e7d7ab4a443df4978ba Mon Sep 17 00:00:00 2001 From: markjcrane Date: Mon, 7 Mar 2016 21:31:37 -0700 Subject: [PATCH] Add http digest authentication and make it the default with an option to use basic if required. --- app/provision/index.php | 94 +++++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/app/provision/index.php b/app/provision/index.php index c346eef992..a0ef5de0ec 100644 --- a/app/provision/index.php +++ b/app/provision/index.php @@ -249,13 +249,75 @@ openlog("fusion-provisioning", LOG_PID | LOG_PERROR, LOG_LOCAL0); } } -//http authentication - if (strlen($provision["http_auth_username"]) > 0 && strlen($provision["http_auth_password"]) > 0) { +//http authentication - digest + if (strlen($provision["http_auth_username"]) > 0 && strlen($provision["http_auth_type"]) == 0) { $provision["http_auth_type"] = "digest"; } + if (strlen($provision["http_auth_username"]) > 0 && strlen($provision["http_auth_password"]) > 0 && $provision["http_auth_type"] == "digest") { + //function to parse the http auth header + function http_digest_parse($txt) { + //protect against missing data + $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); + $data = array(); + $keys = implode('|', array_keys($needed_parts)); + preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER); + foreach ($matches as $m) { + $data[$m[1]] = $m[3] ? $m[3] : $m[4]; + unset($needed_parts[$m[1]]); + } + return $needed_parts ? false : $data; + } + + //function to request digest authentication + function http_digest_request($realm) { + header('HTTP/1.1 401 Authorization Required'); + header('WWW-Authenticate: Digest realm="'.$realm.'", qop="auth", nonce="'.uniqid().'", opaque="'.md5($realm).'"'); + header("Content-Type: text/html"); + $content = 'Authorization Cancelled'; + header("Content-Length: ".strval(strlen($content))); + echo $content; + die(); + } + + //set the realm + $realm = $_SESSION['domain_name']; + + //request authentication + if (empty($_SERVER['PHP_AUTH_DIGEST'])) { + http_digest_request($realm); + } + + //check for valid digest authentication details + if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || ($data['username'] != $provision["http_auth_username"])) { + header('HTTP/1.1 401 Unauthorized'); + header("Content-Type: text/html"); + $content = 'Unauthorized '.$__line__; + header("Content-Length: ".strval(strlen($content))); + echo $content; + exit; + } + + //generate the valid response + $A1 = md5($provision["http_auth_username"] . ':' . $realm . ':' . $provision["http_auth_password"]); + $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']); + $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2); + if ($data['response'] != $valid_response) { + header('HTTP/1.0 401 Unauthorized'); + header("Content-Type: text/html"); + $content = 'Unauthorized '.$__line__; + header("Content-Length: ".strval(strlen($content))); + echo $content; + exit; + } + } + +//http authentication - basic + if (strlen($provision["http_auth_username"]) > 0 && strlen($provision["http_auth_password"]) > 0 && $provision["http_auth_type"] == "basic") { if (!isset($_SERVER['PHP_AUTH_USER'])) { - header('WWW-Authenticate: Basic realm="'.$_SESSION['domain_name']." ".date('r').'"'); - header('HTTP/1.0 401 Unauthorized'); - header("Content-Type: text/plain"); - header("Content-Length: 0"); + header('WWW-Authenticate: Basic realm="'.$_SESSION['domain_name'].'"'); + header('HTTP/1.0 401 Authorization Required'); + header("Content-Type: text/html"); + $content = 'Authorization Required'; + header("Content-Length: ".strval(strlen($content))); + echo $content; exit; } else { if ($_SERVER['PHP_AUTH_USER'] == $provision["http_auth_username"] && $_SERVER['PHP_AUTH_PW'] == $provision["http_auth_password"]) { @@ -263,10 +325,10 @@ openlog("fusion-provisioning", LOG_PID | LOG_PERROR, LOG_LOCAL0); } else { //access denied - header('WWW-Authenticate: Basic realm="'.$_SESSION['domain_name']." ".date('r').'"'); + header('HTTP/1.0 401 Unauthorized'); + header('WWW-Authenticate: Basic realm="'.$_SESSION['domain_name'].'"'); unset($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']); - usleep(rand(1000000,3000000));//1-3 seconds. - $content = 'Authorization Required'; + $content = 'Unauthorized'; header("Content-Length: ".strval(strlen($content))); echo $content; exit; @@ -282,7 +344,6 @@ openlog("fusion-provisioning", LOG_PID | LOG_PERROR, LOG_LOCAL0); openlog('FusionPBX', LOG_NDELAY, LOG_AUTH); syslog(LOG_WARNING, '['.$_SERVER['REMOTE_ADDR']."] provision attempt bad password for ".check_str($_REQUEST['mac'])); closelog(); - usleep(rand(1000000,3000000));//1-3 seconds. echo "access denied 4"; return; } @@ -334,17 +395,4 @@ openlog("fusion-provisioning", LOG_PID | LOG_PERROR, LOG_LOCAL0); echo $file_contents; closelog(); - function http_digest_parse($txt){ - // protect against missing data - $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); - $data = array(); - $keys = implode('|', array_keys($needed_parts)); - preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER); - foreach ($matches as $m) { - $data[$m[1]] = $m[3] ? $m[3] : $m[4]; - unset($needed_parts[$m[1]]); - } - return $needed_parts ? false : $data; - } - ?>