Skip to content

Commit

Permalink
Fix bug #55639: Digest autentication dont work
Browse files Browse the repository at this point in the history
RFC 2617 and 7616 describe that for the "Authorization" header we should
not put the qop nor nc value inside quotes. This differs from the
WWW-Authenticate header, which may have been the source of the confusion
in the implementation. While the version with quotes seems to work fine
in some cases, clearly not all servers accept the non-standard form.
To fix the issue, simply removing the quotes of those two header fields
of the client request to be in line with the RFC suffices.

I refer further to example 3.5 in RFC 2617 and example 3.9.1 in
RFC 7616.

RFC 2617: https://datatracker.ietf.org/doc/html/rfc2617
RFC 7616: https://datatracker.ietf.org/doc/html/rfc7616

Closes GH-14328.
  • Loading branch information
nielsdos committed Jul 17, 2024
1 parent b368db2 commit 911dc5b
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 5 deletions.
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.2.23

- Soap:
. Fixed bug #55639 (Digest autentication dont work). (nielsdos)

01 Aug 2024, PHP 8.2.22

- Core:
Expand Down
10 changes: 5 additions & 5 deletions ext/soap/php_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,7 @@ int make_http_soap_request(zval *this_ptr,
PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8);
PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
/* TODO: Support for qop="auth-int" */
/* TODO: Support for qop=auth-int */
PHP_MD5Update(&md5ctx, (unsigned char*)"auth", sizeof("auth")-1);
PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
}
Expand Down Expand Up @@ -781,11 +781,11 @@ int make_http_soap_request(zval *this_ptr,
}
if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "qop", sizeof("qop")-1)) != NULL &&
Z_TYPE_P(tmp) == IS_STRING) {
/* TODO: Support for qop="auth-int" */
smart_str_append_const(&soap_headers, "\", qop=\"auth");
smart_str_append_const(&soap_headers, "\", nc=\"");
/* TODO: Support for qop=auth-int */
smart_str_append_const(&soap_headers, "\", qop=auth");
smart_str_append_const(&soap_headers, ", nc=");
smart_str_appendl(&soap_headers, nc, 8);
smart_str_append_const(&soap_headers, "\", cnonce=\"");
smart_str_append_const(&soap_headers, ", cnonce=\"");
smart_str_appendl(&soap_headers, cnonce, 8);
}
smart_str_append_const(&soap_headers, "\", response=\"");
Expand Down
65 changes: 65 additions & 0 deletions ext/soap/tests/bugs/bug55639.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
--TEST--
Bug #55639 (Digest authentication dont work)
--INI--
soap.wsdl_cache_enabled=0
--EXTENSIONS--
soap
--SKIPIF--
<?php
if (!file_exists(__DIR__ . "/../../../../sapi/cli/tests/php_cli_server.inc")) {
echo "skip sapi/cli/tests/php_cli_server.inc required but not found";
}
?>
--FILE--
<?php

include __DIR__ . "/../../../../sapi/cli/tests/php_cli_server.inc";

$args = ["-d", "extension_dir=" . ini_get("extension_dir"), "-d", "extension=" . (substr(PHP_OS, 0, 3) == "WIN" ? "php_" : "") . "soap." . PHP_SHLIB_SUFFIX];
if (php_ini_loaded_file()) {
// Necessary such that it works from a development directory in which case extension_dir might not be the real extension dir
$args[] = "-c";
$args[] = php_ini_loaded_file();
}

$code = <<<'PHP'
/* Receive */
header('HTTP/1.0 401 Unauthorized');
header('WWW-Authenticate: Digest realm="realm", qop="auth,auth-int", nonce="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", opaque="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"');
file_get_contents("php://input");
PHP;

php_cli_server_start($code, null, $args);

$client = new soapclient(NULL, [
'location' => 'http://' . PHP_CLI_SERVER_ADDRESS,
'uri' => 'misc-uri',
'authentication' => SOAP_AUTHENTICATION_DIGEST,
'realm' => 'myrealm',
'login' => 'user',
'password' => 'pass',
'trace' => true,
]);

try {
$client->__soapCall("foo", []);
} catch (Throwable $e) {
echo $e->getMessage(), "\n";
}

$headers = $client->__getLastRequestHeaders();
var_dump($headers);

?>
--EXPECTF--
Unauthorized
string(424) "POST / HTTP/1.1
Host: %s
Connection: Keep-Alive
User-Agent: %s
Content-Type: text/xml; charset=utf-8
SOAPAction: "misc-uri#foo"
Content-Length: %d
Authorization: Digest username="user", realm="realm", nonce="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", uri="/", qop=auth, nc=00000001, cnonce="%s", response="%s", opaque="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"

"

0 comments on commit 911dc5b

Please sign in to comment.