From 2ef490e2b0386dbd26a994eca299b393fa2295f1 Mon Sep 17 00:00:00 2001 From: Nikolaus Demmel Date: Sat, 13 May 2023 01:23:18 +0200 Subject: [PATCH 1/2] Add the option for digest authentication for webdav. Signed-off-by: Nikolaus Demmel --- lib/WebDavAuth.php | 74 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/lib/WebDavAuth.php b/lib/WebDavAuth.php index 73c0556..ced28ac 100644 --- a/lib/WebDavAuth.php +++ b/lib/WebDavAuth.php @@ -11,9 +11,9 @@ class WebDavAuth extends Base { private $webDavAuthUrl; - public function __construct($webDavAuthUrl) { - parent::__construct($webDavAuthUrl); + public function __construct($webDavAuthUrl, $authType = 'basic') { $this->webDavAuthUrl = $webDavAuthUrl; + $this->authType = $authType; } /** @@ -31,12 +31,74 @@ public function checkPassword($uid, $password) { return false; } list($protocol, $path) = $arr; - $url = $protocol.'://'.urlencode($uid).':'.urlencode($password).'@'.$path; - $headers = get_headers($url); - if ($headers === false) { - \OC::$server->getLogger()->error('ERROR: Not possible to connect to WebDAV Url: "'.$protocol.'://'.$path.'" ', ['app' => 'user_external']); + $url = $protocol.'://'.$path; + + switch ($this->authType) { + case 'digest': + // Initial unauthenticated request + @file_get_contents($url); + if (!isset($http_response_header)) { + \OC::$server->getLogger()->error('ERROR: Not possible to connect to WebDAV Url: "'.$protocol.'://'.$path.'" ', ['app' => 'user_external']); + return false; + } + + // Find the WWW-Authenticate header + foreach ($http_response_header as $header) { + if (strpos($header, 'WWW-Authenticate: Digest') === 0) { + $auth_header = substr($header, strlen('WWW-Authenticate: Digest ')); + break; + } + } + + // Parse the header to get the parameters + $auth_params = array(); + foreach (explode(',', $auth_header) as $param) { + list($key, $value) = explode('=', $param, 2); + $auth_params[trim($key)] = trim($value, ' "'); + } + + // Generate a cnonce (client nonce) value + $cnonce = bin2hex(openssl_random_pseudo_bytes(8)); + + // Generate the response value + $A1 = md5($uid . ':' . $auth_params['realm'] . ':' . $password); + $A2 = md5('GET:' . $url); + $response = md5($A1 . ':' . $auth_params['nonce'] . ':00000001:' . $cnonce . ':auth:' . $A2); + + // Construct the Authorization header + $auth_header = 'Authorization: Digest username="' . $uid . '", realm="' . $auth_params['realm'] . + '", nonce="' . $auth_params['nonce'] . '", uri="' . $url . '", cnonce="' . $cnonce . + '", nc=00000001, qop=auth, response="' . $response . '", opaque="' . $auth_params['opaque'] . '"'; + + // Make the authenticated request + $context = stream_context_create(array( + 'http' => array( + 'method' => 'GET', + 'header' => $auth_header + ) + )); + break; + case 'basic': + $url = $protocol.'://'.urlencode($uid).':'.urlencode($password).'@'.$path; + $context = stream_context_create(array( + 'http' => array( + 'method' => 'GET', + 'header' => 'Authorization: Basic ' . base64_encode($uid . ':' . $password) + ) + )); + break; + default: + \OC::$server->getLogger()->error('ERROR: Invalid authentication type: "'.$this->authType.'". Expected "basic" or "digest".', ['app' => 'user_external']); + return false; + } + + $result = @file_get_contents($url, false, $context); + if ($result === false) { + \OC::$server->getLogger()->error('ERROR: Not possible to connect to WebDAV Url: "'.$url.'" ', ['app' => 'user_external']); return false; } + + $headers = $http_response_header; $returnCode = substr($headers[0], 9, 3); if (substr($returnCode, 0, 1) === '2') { From 2bfb478f1e48d66c4ccdd18596769a9934fa2cf5 Mon Sep 17 00:00:00 2001 From: Nikolaus Demmel Date: Sat, 13 May 2023 01:29:10 +0200 Subject: [PATCH 2/2] Update Readme Signed-off-by: Nikolaus Demmel --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 76f0001..2e66c74 100644 --- a/README.md +++ b/README.md @@ -131,8 +131,11 @@ Add the following to your `config.php`: 'user_backends' => array( array( - 'class' => '\OCA\UserExternal\WebDAVAuth', - 'arguments' => array('https://example.com/webdav'), + 'class' => '\OCA\UserExternal\WebDavAuth', + 'arguments' => array( + 'https://example.com/webdav', + 'basic', // alternative: 'digest' + ), ), ),