| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2015, 2016, 2017 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | // Package cmd This file implements helper functions to validate AWS
 | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | // Signature Version '4' authorization header.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This package provides comprehensive helpers for following signature
 | 
					
						
							|  |  |  | // types.
 | 
					
						
							|  |  |  | // - Based on Authorization header.
 | 
					
						
							|  |  |  | // - Based on Query parameters.
 | 
					
						
							|  |  |  | // - Based on Form POST policy.
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-01-02 14:30:02 +08:00
										 |  |  | 	"crypto/subtle" | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	"encoding/hex" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-30 07:35:12 +08:00
										 |  |  | 	"github.com/minio/minio-go/v6/pkg/s3utils" | 
					
						
							| 
									
										
										
										
											2018-01-02 14:30:02 +08:00
										 |  |  | 	sha256 "github.com/minio/sha256-simd" | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | // AWS Signature Version '4' constants.
 | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2017-07-08 05:31:42 +08:00
										 |  |  | 	signV4Algorithm = "AWS4-HMAC-SHA256" | 
					
						
							|  |  |  | 	iso8601Format   = "20060102T150405Z" | 
					
						
							|  |  |  | 	yyyymmdd        = "20060102" | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | type serviceType string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	serviceS3  serviceType = "s3" | 
					
						
							|  |  |  | 	serviceSTS serviceType = "sts" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | // getCanonicalHeaders generate a list of request headers with their values
 | 
					
						
							| 
									
										
										
										
											2017-04-06 08:00:24 +08:00
										 |  |  | func getCanonicalHeaders(signedHeaders http.Header) string { | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	var headers []string | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	vals := make(http.Header) | 
					
						
							| 
									
										
										
										
											2015-07-11 08:21:53 +08:00
										 |  |  | 	for k, vv := range signedHeaders { | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 		headers = append(headers, strings.ToLower(k)) | 
					
						
							|  |  |  | 		vals[strings.ToLower(k)] = vv | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sort.Strings(headers) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var buf bytes.Buffer | 
					
						
							|  |  |  | 	for _, k := range headers { | 
					
						
							|  |  |  | 		buf.WriteString(k) | 
					
						
							|  |  |  | 		buf.WriteByte(':') | 
					
						
							| 
									
										
										
										
											2017-04-06 08:00:24 +08:00
										 |  |  | 		for idx, v := range vals[k] { | 
					
						
							|  |  |  | 			if idx > 0 { | 
					
						
							|  |  |  | 				buf.WriteByte(',') | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-04-06 08:00:24 +08:00
										 |  |  | 			buf.WriteString(signV4TrimAll(v)) | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-06 08:00:24 +08:00
										 |  |  | 		buf.WriteByte('\n') | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return buf.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getSignedHeaders generate a string i.e alphabetically sorted, semicolon-separated list of lowercase request header names
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | func getSignedHeaders(signedHeaders http.Header) string { | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	var headers []string | 
					
						
							| 
									
										
										
										
											2015-07-11 08:21:53 +08:00
										 |  |  | 	for k := range signedHeaders { | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 		headers = append(headers, strings.ToLower(k)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sort.Strings(headers) | 
					
						
							|  |  |  | 	return strings.Join(headers, ";") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getCanonicalRequest generate a canonical request of style
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // canonicalRequest =
 | 
					
						
							|  |  |  | //  <HTTPMethod>\n
 | 
					
						
							|  |  |  | //  <CanonicalURI>\n
 | 
					
						
							|  |  |  | //  <CanonicalQueryString>\n
 | 
					
						
							|  |  |  | //  <CanonicalHeaders>\n
 | 
					
						
							|  |  |  | //  <SignedHeaders>\n
 | 
					
						
							|  |  |  | //  <HashedPayload>
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2017-04-06 08:00:24 +08:00
										 |  |  | func getCanonicalRequest(extractedSignedHeaders http.Header, payload, queryStr, urlPath, method string) string { | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	rawQuery := strings.Replace(queryStr, "+", "%20", -1) | 
					
						
							| 
									
										
										
										
											2018-06-06 01:48:51 +08:00
										 |  |  | 	encodedPath := s3utils.EncodePath(urlPath) | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	canonicalRequest := strings.Join([]string{ | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 		method, | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 		encodedPath, | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 		rawQuery, | 
					
						
							| 
									
										
										
										
											2017-04-06 08:00:24 +08:00
										 |  |  | 		getCanonicalHeaders(extractedSignedHeaders), | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 		getSignedHeaders(extractedSignedHeaders), | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | 		payload, | 
					
						
							|  |  |  | 	}, "\n") | 
					
						
							|  |  |  | 	return canonicalRequest | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | // getScope generate a string of a specific date, an AWS region, and a service.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | func getScope(t time.Time, region string) string { | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	scope := strings.Join([]string{ | 
					
						
							|  |  |  | 		t.Format(yyyymmdd), | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 		region, | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 		string(serviceS3), | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 		"aws4_request", | 
					
						
							|  |  |  | 	}, "/") | 
					
						
							|  |  |  | 	return scope | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | // getStringToSign a string based on selected query values.
 | 
					
						
							| 
									
										
										
										
											2017-02-07 05:09:09 +08:00
										 |  |  | func getStringToSign(canonicalRequest string, t time.Time, scope string) string { | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	stringToSign := signV4Algorithm + "\n" + t.Format(iso8601Format) + "\n" | 
					
						
							| 
									
										
										
										
											2017-02-07 05:09:09 +08:00
										 |  |  | 	stringToSign = stringToSign + scope + "\n" | 
					
						
							| 
									
										
										
										
											2016-02-11 08:40:09 +08:00
										 |  |  | 	canonicalRequestBytes := sha256.Sum256([]byte(canonicalRequest)) | 
					
						
							|  |  |  | 	stringToSign = stringToSign + hex.EncodeToString(canonicalRequestBytes[:]) | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	return stringToSign | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | // getSigningKey hmac seed to calculate final signature.
 | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | func getSigningKey(secretKey string, t time.Time, region string, stype serviceType) []byte { | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	date := sumHMAC([]byte("AWS4"+secretKey), []byte(t.Format(yyyymmdd))) | 
					
						
							|  |  |  | 	regionBytes := sumHMAC(date, []byte(region)) | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 	service := sumHMAC(regionBytes, []byte(stype)) | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	signingKey := sumHMAC(service, []byte("aws4_request")) | 
					
						
							|  |  |  | 	return signingKey | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | // getSignature final signature in hexadecimal form.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | func getSignature(signingKey []byte, stringToSign string) string { | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	return hex.EncodeToString(sumHMAC(signingKey, []byte(stringToSign))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | // Check to see if Policy is signed correctly.
 | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | func doesPolicySignatureMatch(formValues http.Header) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 	// For SignV2 - Signature field will be valid
 | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 	if _, ok := formValues["Signature"]; ok { | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | 		return doesPolicySignatureV2Match(formValues) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return doesPolicySignatureV4Match(formValues) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-02 14:30:02 +08:00
										 |  |  | // compareSignatureV4 returns true if and only if both signatures
 | 
					
						
							|  |  |  | // are equal. The signatures are expected to be HEX encoded strings
 | 
					
						
							|  |  |  | // according to the AWS S3 signature V4 spec.
 | 
					
						
							|  |  |  | func compareSignatureV4(sig1, sig2 string) bool { | 
					
						
							|  |  |  | 	// The CTC using []byte(str) works because the hex encoding
 | 
					
						
							|  |  |  | 	// is unique for a sequence of bytes. See also compareSignatureV2.
 | 
					
						
							|  |  |  | 	return subtle.ConstantTimeCompare([]byte(sig1), []byte(sig2)) == 1 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | // doesPolicySignatureMatch - Verify query headers with post policy
 | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | //     - http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html
 | 
					
						
							| 
									
										
										
										
											2016-10-12 16:03:50 +08:00
										 |  |  | // returns ErrNone if the signature matches.
 | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | func doesPolicySignatureV4Match(formValues http.Header) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Server region.
 | 
					
						
							| 
									
										
										
										
											2017-11-30 05:12:47 +08:00
										 |  |  | 	region := globalServerConfig.GetRegion() | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Parse credential tag.
 | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 	credHeader, err := parseCredentialHeader("Credential="+formValues.Get("X-Amz-Credential"), region, serviceS3) | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return ErrMissingFields | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-07 22:40:03 +08:00
										 |  |  | 	cred, _, s3Err := checkKeyValid(credHeader.accessKey) | 
					
						
							|  |  |  | 	if s3Err != ErrNone { | 
					
						
							|  |  |  | 		return s3Err | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Get signing key.
 | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 	signingKey := getSigningKey(cred.SecretKey, credHeader.scope.date, credHeader.scope.region, serviceS3) | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Get signature.
 | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 	newSignature := getSignature(signingKey, formValues.Get("Policy")) | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Verify signature.
 | 
					
						
							| 
									
										
										
										
											2018-01-02 14:30:02 +08:00
										 |  |  | 	if !compareSignatureV4(newSignature, formValues.Get("X-Amz-Signature")) { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrSignatureDoesNotMatch | 
					
						
							| 
									
										
										
										
											2015-10-02 14:51:17 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Success.
 | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	return ErrNone | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | // doesPresignedSignatureMatch - Verify query headers with presigned signature
 | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | //     - http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
 | 
					
						
							| 
									
										
										
										
											2016-10-12 16:03:50 +08:00
										 |  |  | // returns ErrNone if the signature matches.
 | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region string, stype serviceType) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Copy request
 | 
					
						
							|  |  |  | 	req := *r | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Parse request query string.
 | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 	pSignValues, err := parsePreSignV4(req.URL.Query(), region, stype) | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-07 22:40:03 +08:00
										 |  |  | 	cred, _, s3Err := checkKeyValid(pSignValues.Credential.accessKey) | 
					
						
							|  |  |  | 	if s3Err != ErrNone { | 
					
						
							|  |  |  | 		return s3Err | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Extract all the signed headers along with its values.
 | 
					
						
							| 
									
										
										
										
											2017-04-06 06:08:33 +08:00
										 |  |  | 	extractedSignedHeaders, errCode := extractSignedHeaders(pSignValues.SignedHeaders, r) | 
					
						
							| 
									
										
										
										
											2016-08-10 00:13:15 +08:00
										 |  |  | 	if errCode != ErrNone { | 
					
						
							|  |  |  | 		return errCode | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-07 22:40:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Construct new query.
 | 
					
						
							|  |  |  | 	query := make(url.Values) | 
					
						
							| 
									
										
										
										
											2017-04-11 00:58:08 +08:00
										 |  |  | 	if req.URL.Query().Get("X-Amz-Content-Sha256") != "" { | 
					
						
							| 
									
										
										
										
											2016-04-07 18:04:18 +08:00
										 |  |  | 		query.Set("X-Amz-Content-Sha256", hashedPayload) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-05 16:04:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	query.Set("X-Amz-Algorithm", signV4Algorithm) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-13 08:32:00 +08:00
										 |  |  | 	// If the host which signed the request is slightly ahead in time (by less than globalMaxSkewTime) the
 | 
					
						
							|  |  |  | 	// request should still be allowed.
 | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	if pSignValues.Date.After(UTCNow().Add(globalMaxSkewTime)) { | 
					
						
							| 
									
										
										
										
											2016-09-10 15:38:07 +08:00
										 |  |  | 		return ErrRequestNotReadyYet | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-19 02:45:16 +08:00
										 |  |  | 	if UTCNow().Sub(pSignValues.Date) > pSignValues.Expires { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrExpiredPresignRequest | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Save the date and expires.
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | 	t := pSignValues.Date | 
					
						
							| 
									
										
										
										
											2017-08-19 02:45:16 +08:00
										 |  |  | 	expireSeconds := int(pSignValues.Expires / time.Second) | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-18 18:13:52 +08:00
										 |  |  | 	// Construct the query.
 | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | 	query.Set("X-Amz-Date", t.Format(iso8601Format)) | 
					
						
							|  |  |  | 	query.Set("X-Amz-Expires", strconv.Itoa(expireSeconds)) | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	query.Set("X-Amz-SignedHeaders", getSignedHeaders(extractedSignedHeaders)) | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 	query.Set("X-Amz-Credential", cred.AccessKey+"/"+pSignValues.Credential.getScope()) | 
					
						
							| 
									
										
										
										
											2016-02-07 19:37:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Save other headers available in the request parameters.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	for k, v := range req.URL.Query() { | 
					
						
							| 
									
										
										
										
											2019-05-22 12:00:02 +08:00
										 |  |  | 		key := strings.ToLower(k) | 
					
						
							| 
									
										
										
										
											2018-07-11 11:27:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Handle the metadata in presigned put query string
 | 
					
						
							| 
									
										
										
										
											2019-05-22 12:00:02 +08:00
										 |  |  | 		if strings.Contains(key, "x-amz-meta-") { | 
					
						
							|  |  |  | 			query.Set(k, v[0]) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if strings.Contains(key, "x-amz-server-side-") { | 
					
						
							| 
									
										
										
										
											2018-07-11 11:27:10 +08:00
										 |  |  | 			query.Set(k, v[0]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 12:00:02 +08:00
										 |  |  | 		if strings.HasPrefix(key, "x-amz") { | 
					
						
							| 
									
										
										
										
											2016-02-07 19:37:54 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		query[k] = v | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-18 18:13:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Get the encoded query.
 | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | 	encodedQuery := query.Encode() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-29 03:55:00 +08:00
										 |  |  | 	// Verify if date query is same.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	if req.URL.Query().Get("X-Amz-Date") != query.Get("X-Amz-Date") { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrSignatureDoesNotMatch | 
					
						
							| 
									
										
										
										
											2016-01-29 03:55:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Verify if expires query is same.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	if req.URL.Query().Get("X-Amz-Expires") != query.Get("X-Amz-Expires") { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrSignatureDoesNotMatch | 
					
						
							| 
									
										
										
										
											2016-01-29 03:55:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Verify if signed headers query is same.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	if req.URL.Query().Get("X-Amz-SignedHeaders") != query.Get("X-Amz-SignedHeaders") { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrSignatureDoesNotMatch | 
					
						
							| 
									
										
										
										
											2016-01-29 03:55:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Verify if credential query is same.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	if req.URL.Query().Get("X-Amz-Credential") != query.Get("X-Amz-Credential") { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrSignatureDoesNotMatch | 
					
						
							| 
									
										
										
										
											2016-01-29 03:55:00 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-07 18:04:18 +08:00
										 |  |  | 	// Verify if sha256 payload query is same.
 | 
					
						
							|  |  |  | 	if req.URL.Query().Get("X-Amz-Content-Sha256") != "" { | 
					
						
							|  |  |  | 		if req.URL.Query().Get("X-Amz-Content-Sha256") != query.Get("X-Amz-Content-Sha256") { | 
					
						
							| 
									
										
										
										
											2018-03-10 10:18:57 +08:00
										 |  |  | 			return ErrContentSHA256Mismatch | 
					
						
							| 
									
										
										
										
											2016-04-07 18:04:18 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/// Verify finally if signature is same.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get canonical request.
 | 
					
						
							| 
									
										
										
										
											2017-11-17 03:13:04 +08:00
										 |  |  | 	presignedCanonicalReq := getCanonicalRequest(extractedSignedHeaders, hashedPayload, encodedQuery, req.URL.Path, req.Method) | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Get string to sign from canonical request.
 | 
					
						
							| 
									
										
										
										
											2017-02-07 05:09:09 +08:00
										 |  |  | 	presignedStringToSign := getStringToSign(presignedCanonicalReq, t, pSignValues.Credential.getScope()) | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Get hmac presigned signing key.
 | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 	presignedSigningKey := getSigningKey(cred.SecretKey, pSignValues.Credential.scope.date, | 
					
						
							|  |  |  | 		pSignValues.Credential.scope.region, stype) | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Get new signature.
 | 
					
						
							|  |  |  | 	newSignature := getSignature(presignedSigningKey, presignedStringToSign) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Verify signature.
 | 
					
						
							| 
									
										
										
										
											2018-01-02 14:30:02 +08:00
										 |  |  | 	if !compareSignatureV4(req.URL.Query().Get("X-Amz-Signature"), newSignature) { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrSignatureDoesNotMatch | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	return ErrNone | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | // doesSignatureMatch - Verify authorization header with calculated header in accordance with
 | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | //     - http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html
 | 
					
						
							| 
									
										
										
										
											2016-10-12 16:03:50 +08:00
										 |  |  | // returns ErrNone if signature matches.
 | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | func doesSignatureMatch(hashedPayload string, r *http.Request, region string, stype serviceType) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Copy request.
 | 
					
						
							|  |  |  | 	req := *r | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Save authorization header.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	v4Auth := req.Header.Get("Authorization") | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Parse signature version '4' header.
 | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 	signV4Values, err := parseSignV4(v4Auth, region, stype) | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Extract all the signed headers along with its values.
 | 
					
						
							| 
									
										
										
										
											2017-04-06 06:08:33 +08:00
										 |  |  | 	extractedSignedHeaders, errCode := extractSignedHeaders(signV4Values.SignedHeaders, r) | 
					
						
							| 
									
										
										
										
											2016-08-10 00:13:15 +08:00
										 |  |  | 	if errCode != ErrNone { | 
					
						
							|  |  |  | 		return errCode | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-07 22:40:03 +08:00
										 |  |  | 	cred, _, s3Err := checkKeyValid(signV4Values.Credential.accessKey) | 
					
						
							|  |  |  | 	if s3Err != ErrNone { | 
					
						
							|  |  |  | 		return s3Err | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-07-10 05:42:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Extract date, if not present throw error.
 | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	var date string | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	if date = req.Header.Get(http.CanonicalHeaderKey("x-amz-date")); date == "" { | 
					
						
							|  |  |  | 		if date = r.Header.Get("Date"); date == "" { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 			return ErrMissingDateHeader | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-07 22:40:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Parse date header.
 | 
					
						
							|  |  |  | 	t, e := time.Parse(iso8601Format, date) | 
					
						
							|  |  |  | 	if e != nil { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrMalformedDate | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Query string.
 | 
					
						
							|  |  |  | 	queryStr := req.URL.Query().Encode() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get canonical request.
 | 
					
						
							| 
									
										
										
										
											2017-04-06 08:00:24 +08:00
										 |  |  | 	canonicalRequest := getCanonicalRequest(extractedSignedHeaders, hashedPayload, queryStr, req.URL.Path, req.Method) | 
					
						
							| 
									
										
										
										
											2017-04-11 00:58:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Get string to sign from canonical request.
 | 
					
						
							| 
									
										
										
										
											2017-02-07 05:09:09 +08:00
										 |  |  | 	stringToSign := getStringToSign(canonicalRequest, t, signV4Values.Credential.getScope()) | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Get hmac signing key.
 | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 	signingKey := getSigningKey(cred.SecretKey, signV4Values.Credential.scope.date, | 
					
						
							|  |  |  | 		signV4Values.Credential.scope.region, stype) | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Calculate signature.
 | 
					
						
							|  |  |  | 	newSignature := getSignature(signingKey, stringToSign) | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Verify if signature match.
 | 
					
						
							| 
									
										
										
										
											2018-01-02 14:30:02 +08:00
										 |  |  | 	if !compareSignatureV4(newSignature, signV4Values.Signature) { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrSignatureDoesNotMatch | 
					
						
							| 
									
										
										
										
											2015-07-10 05:42:04 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Return error none.
 | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	return ErrNone | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | } |