]> git.mxchange.org Git - friendica-addons.git/blob - dav/SabreDAV/lib/Sabre/HTTP/AWSAuth.php
Merge remote branch 'upstream/master'
[friendica-addons.git] / dav / SabreDAV / lib / Sabre / HTTP / AWSAuth.php
1 <?php
2
3 /**
4  * HTTP AWS Authentication handler
5  *
6  * Use this class to leverage amazon's AWS authentication header
7  *
8  * @package Sabre
9  * @subpackage HTTP
10  * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
11  * @author Evert Pot (http://www.rooftopsolutions.nl/)
12  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
13  */
14 class Sabre_HTTP_AWSAuth extends Sabre_HTTP_AbstractAuth {
15
16     /**
17      * The signature supplied by the HTTP client
18      *
19      * @var string
20      */
21     private $signature = null;
22
23     /**
24      * The accesskey supplied by the HTTP client
25      *
26      * @var string
27      */
28     private $accessKey = null;
29
30     /**
31      * An error code, if any
32      *
33      * This value will be filled with one of the ERR_* constants
34      *
35      * @var int
36      */
37     public $errorCode = 0;
38
39     const ERR_NOAWSHEADER = 1;
40     const ERR_MD5CHECKSUMWRONG = 2;
41     const ERR_INVALIDDATEFORMAT = 3;
42     const ERR_REQUESTTIMESKEWED = 4;
43     const ERR_INVALIDSIGNATURE = 5;
44
45     /**
46      * Gathers all information from the headers
47      *
48      * This method needs to be called prior to anything else.
49      *
50      * @return bool
51      */
52     public function init() {
53
54         $authHeader = $this->httpRequest->getHeader('Authorization');
55         $authHeader = explode(' ',$authHeader);
56
57         if ($authHeader[0]!='AWS' || !isset($authHeader[1])) {
58             $this->errorCode = self::ERR_NOAWSHEADER;
59              return false;
60         }
61
62         list($this->accessKey,$this->signature) = explode(':',$authHeader[1]);
63
64         return true;
65
66     }
67
68     /**
69      * Returns the username for the request
70      *
71      * @return string
72      */
73     public function getAccessKey() {
74
75         return $this->accessKey;
76
77     }
78
79     /**
80      * Validates the signature based on the secretKey
81      *
82      * @param string $secretKey
83      * @return bool
84      */
85     public function validate($secretKey) {
86
87         $contentMD5 = $this->httpRequest->getHeader('Content-MD5');
88
89         if ($contentMD5) {
90             // We need to validate the integrity of the request
91             $body = $this->httpRequest->getBody(true);
92             $this->httpRequest->setBody($body,true);
93
94             if ($contentMD5!=base64_encode(md5($body,true))) {
95                 // content-md5 header did not match md5 signature of body
96                 $this->errorCode = self::ERR_MD5CHECKSUMWRONG;
97                 return false;
98             }
99
100         }
101
102         if (!$requestDate = $this->httpRequest->getHeader('x-amz-date'))
103             $requestDate = $this->httpRequest->getHeader('Date');
104
105         if (!$this->validateRFC2616Date($requestDate))
106             return false;
107
108         $amzHeaders = $this->getAmzHeaders();
109
110         $signature = base64_encode(
111             $this->hmacsha1($secretKey,
112                 $this->httpRequest->getMethod() . "\n" .
113                 $contentMD5 . "\n" .
114                 $this->httpRequest->getHeader('Content-type') . "\n" .
115                 $requestDate . "\n" .
116                 $amzHeaders .
117                 $this->httpRequest->getURI()
118             )
119         );
120
121         if ($this->signature != $signature) {
122
123             $this->errorCode = self::ERR_INVALIDSIGNATURE;
124             return false;
125
126         }
127
128         return true;
129
130     }
131
132
133     /**
134      * Returns an HTTP 401 header, forcing login
135      *
136      * This should be called when username and password are incorrect, or not supplied at all
137      *
138      * @return void
139      */
140     public function requireLogin() {
141
142         $this->httpResponse->setHeader('WWW-Authenticate','AWS');
143         $this->httpResponse->sendStatus(401);
144
145     }
146
147     /**
148      * Makes sure the supplied value is a valid RFC2616 date.
149      *
150      * If we would just use strtotime to get a valid timestamp, we have no way of checking if a
151      * user just supplied the word 'now' for the date header.
152      *
153      * This function also makes sure the Date header is within 15 minutes of the operating
154      * system date, to prevent replay attacks.
155      *
156      * @param string $dateHeader
157      * @return bool
158      */
159     protected function validateRFC2616Date($dateHeader) {
160
161         $date = Sabre_HTTP_Util::parseHTTPDate($dateHeader);
162
163         // Unknown format
164         if (!$date) {
165             $this->errorCode = self::ERR_INVALIDDATEFORMAT;
166             return false;
167         }
168
169         $min = new DateTime('-15 minutes');
170         $max = new DateTime('+15 minutes');
171
172         // We allow 15 minutes around the current date/time
173         if ($date > $max || $date < $min) {
174             $this->errorCode = self::ERR_REQUESTTIMESKEWED;
175             return false;
176         }
177
178         return $date;
179
180     }
181
182     /**
183      * Returns a list of AMZ headers
184      *
185      * @return string
186      */
187     protected function getAmzHeaders() {
188
189         $amzHeaders = array();
190         $headers = $this->httpRequest->getHeaders();
191         foreach($headers as $headerName => $headerValue) {
192             if (strpos(strtolower($headerName),'x-amz-')===0) {
193                 $amzHeaders[strtolower($headerName)] = str_replace(array("\r\n"),array(' '),$headerValue) . "\n";
194             }
195         }
196         ksort($amzHeaders);
197
198         $headerStr = '';
199         foreach($amzHeaders as $h=>$v) {
200             $headerStr.=$h.':'.$v;
201         }
202
203         return $headerStr;
204
205     }
206
207     /**
208      * Generates an HMAC-SHA1 signature
209      *
210      * @param string $key
211      * @param string $message
212      * @return string
213      */
214     private function hmacsha1($key, $message) {
215
216         $blocksize=64;
217         if (strlen($key)>$blocksize)
218             $key=pack('H*', sha1($key));
219         $key=str_pad($key,$blocksize,chr(0x00));
220         $ipad=str_repeat(chr(0x36),$blocksize);
221         $opad=str_repeat(chr(0x5c),$blocksize);
222         $hmac = pack('H*',sha1(($key^$opad).pack('H*',sha1(($key^$ipad).$message))));
223         return $hmac;
224
225     }
226
227 }