OpenXE/vendor/sabre/http/lib/Auth/AWS.php
2021-05-21 08:49:41 +02:00

235 lines
5.7 KiB
PHP

<?php
namespace Sabre\HTTP\Auth;
use Sabre\HTTP\Util;
/**
* HTTP AWS Authentication handler
*
* Use this class to leverage amazon's AWS authentication header
*
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
class AWS extends AbstractAuth {
/**
* The signature supplied by the HTTP client
*
* @var string
*/
private $signature = null;
/**
* The accesskey supplied by the HTTP client
*
* @var string
*/
private $accessKey = null;
/**
* An error code, if any
*
* This value will be filled with one of the ERR_* constants
*
* @var int
*/
public $errorCode = 0;
const ERR_NOAWSHEADER = 1;
const ERR_MD5CHECKSUMWRONG = 2;
const ERR_INVALIDDATEFORMAT = 3;
const ERR_REQUESTTIMESKEWED = 4;
const ERR_INVALIDSIGNATURE = 5;
/**
* Gathers all information from the headers
*
* This method needs to be called prior to anything else.
*
* @return bool
*/
function init() {
$authHeader = $this->request->getHeader('Authorization');
$authHeader = explode(' ', $authHeader);
if ($authHeader[0] != 'AWS' || !isset($authHeader[1])) {
$this->errorCode = self::ERR_NOAWSHEADER;
return false;
}
list($this->accessKey, $this->signature) = explode(':', $authHeader[1]);
return true;
}
/**
* Returns the username for the request
*
* @return string
*/
function getAccessKey() {
return $this->accessKey;
}
/**
* Validates the signature based on the secretKey
*
* @param string $secretKey
* @return bool
*/
function validate($secretKey) {
$contentMD5 = $this->request->getHeader('Content-MD5');
if ($contentMD5) {
// We need to validate the integrity of the request
$body = $this->request->getBody();
$this->request->setBody($body);
if ($contentMD5 != base64_encode(md5($body, true))) {
// content-md5 header did not match md5 signature of body
$this->errorCode = self::ERR_MD5CHECKSUMWRONG;
return false;
}
}
if (!$requestDate = $this->request->getHeader('x-amz-date'))
$requestDate = $this->request->getHeader('Date');
if (!$this->validateRFC2616Date($requestDate))
return false;
$amzHeaders = $this->getAmzHeaders();
$signature = base64_encode(
$this->hmacsha1($secretKey,
$this->request->getMethod() . "\n" .
$contentMD5 . "\n" .
$this->request->getHeader('Content-type') . "\n" .
$requestDate . "\n" .
$amzHeaders .
$this->request->getUrl()
)
);
if ($this->signature != $signature) {
$this->errorCode = self::ERR_INVALIDSIGNATURE;
return false;
}
return true;
}
/**
* Returns an HTTP 401 header, forcing login
*
* This should be called when username and password are incorrect, or not supplied at all
*
* @return void
*/
function requireLogin() {
$this->response->addHeader('WWW-Authenticate', 'AWS');
$this->response->setStatus(401);
}
/**
* Makes sure the supplied value is a valid RFC2616 date.
*
* If we would just use strtotime to get a valid timestamp, we have no way of checking if a
* user just supplied the word 'now' for the date header.
*
* This function also makes sure the Date header is within 15 minutes of the operating
* system date, to prevent replay attacks.
*
* @param string $dateHeader
* @return bool
*/
protected function validateRFC2616Date($dateHeader) {
$date = Util::parseHTTPDate($dateHeader);
// Unknown format
if (!$date) {
$this->errorCode = self::ERR_INVALIDDATEFORMAT;
return false;
}
$min = new \DateTime('-15 minutes');
$max = new \DateTime('+15 minutes');
// We allow 15 minutes around the current date/time
if ($date > $max || $date < $min) {
$this->errorCode = self::ERR_REQUESTTIMESKEWED;
return false;
}
return $date;
}
/**
* Returns a list of AMZ headers
*
* @return string
*/
protected function getAmzHeaders() {
$amzHeaders = [];
$headers = $this->request->getHeaders();
foreach ($headers as $headerName => $headerValue) {
if (strpos(strtolower($headerName), 'x-amz-') === 0) {
$amzHeaders[strtolower($headerName)] = str_replace(["\r\n"], [' '], $headerValue[0]) . "\n";
}
}
ksort($amzHeaders);
$headerStr = '';
foreach ($amzHeaders as $h => $v) {
$headerStr .= $h . ':' . $v;
}
return $headerStr;
}
/**
* Generates an HMAC-SHA1 signature
*
* @param string $key
* @param string $message
* @return string
*/
private function hmacsha1($key, $message) {
if (function_exists('hash_hmac')) {
return hash_hmac('sha1', $message, $key, true);
}
$blocksize = 64;
if (strlen($key) > $blocksize) {
$key = pack('H*', sha1($key));
}
$key = str_pad($key, $blocksize, chr(0x00));
$ipad = str_repeat(chr(0x36), $blocksize);
$opad = str_repeat(chr(0x5c), $blocksize);
$hmac = pack('H*', sha1(($key ^ $opad) . pack('H*', sha1(($key ^ $ipad) . $message))));
return $hmac;
}
}