| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  |  * Minio Cloud Storage, (C) 2015, 2016 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" | 
					
						
							|  |  |  | 	"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
										 |  |  | 
 | 
					
						
							|  |  |  | 	"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-01-19 04:24:34 +08:00
										 |  |  | 	signV4Algorithm     = "AWS4-HMAC-SHA256" | 
					
						
							|  |  |  | 	iso8601Format       = "20060102T150405Z" | 
					
						
							|  |  |  | 	yyyymmdd            = "20060102" | 
					
						
							|  |  |  | 	presignedHostHeader = "host" | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getCanonicalHeaders generate a list of request headers with their values
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | func getCanonicalHeaders(signedHeaders http.Header, host string) 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 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 	headers = append(headers, presignedHostHeader) | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	sort.Strings(headers) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var buf bytes.Buffer | 
					
						
							|  |  |  | 	for _, k := range headers { | 
					
						
							|  |  |  | 		buf.WriteString(k) | 
					
						
							|  |  |  | 		buf.WriteByte(':') | 
					
						
							|  |  |  | 		switch { | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 		case k == presignedHostHeader: | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 			buf.WriteString(host) | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 			fallthrough | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			for idx, v := range vals[k] { | 
					
						
							|  |  |  | 				if idx > 0 { | 
					
						
							|  |  |  | 					buf.WriteByte(',') | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-11-04 07:41:25 +08:00
										 |  |  | 				buf.WriteString(signV4TrimAll(v)) | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			buf.WriteByte('\n') | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	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)) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 	headers = append(headers, presignedHostHeader) | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	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>
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | func getCanonicalRequest(extractedSignedHeaders http.Header, payload, queryStr, urlPath, method, host string) string { | 
					
						
							|  |  |  | 	rawQuery := strings.Replace(queryStr, "+", "%20", -1) | 
					
						
							|  |  |  | 	encodedPath := getURLEncodedName(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, | 
					
						
							|  |  |  | 		getCanonicalHeaders(extractedSignedHeaders, host), | 
					
						
							|  |  |  | 		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, | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 		"s3", | 
					
						
							|  |  |  | 		"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.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | func getSigningKey(secretKey string, t time.Time, region string) []byte { | 
					
						
							|  |  |  | 	date := sumHMAC([]byte("AWS4"+secretKey), []byte(t.Format(yyyymmdd))) | 
					
						
							|  |  |  | 	regionBytes := sumHMAC(date, []byte(region)) | 
					
						
							|  |  |  | 	service := sumHMAC(regionBytes, []byte("s3")) | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							|  |  |  | func doesPolicySignatureMatch(formValues map[string]string) APIErrorCode { | 
					
						
							|  |  |  | 	// For SignV2 - Signature field will be valid
 | 
					
						
							|  |  |  | 	if formValues["Signature"] != "" { | 
					
						
							|  |  |  | 		return doesPolicySignatureV2Match(formValues) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return doesPolicySignatureV4Match(formValues) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2016-10-22 23:57:12 +08:00
										 |  |  | func doesPolicySignatureV4Match(formValues map[string]string) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Access credentials.
 | 
					
						
							|  |  |  | 	cred := serverConfig.GetCredential() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Server region.
 | 
					
						
							|  |  |  | 	region := serverConfig.GetRegion() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Parse credential tag.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	credHeader, err := parseCredentialHeader("Credential=" + formValues["X-Amz-Credential"]) | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return ErrMissingFields | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Verify if the access key id matches.
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 	if credHeader.accessKey != cred.AccessKey { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrInvalidAccessKeyID | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Verify if the region is valid.
 | 
					
						
							| 
									
										
										
										
											2016-04-02 13:45:27 +08:00
										 |  |  | 	sRegion := credHeader.scope.region | 
					
						
							|  |  |  | 	if !isValidRegion(sRegion, region) { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrInvalidRegion | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Get signing key.
 | 
					
						
							| 
									
										
										
										
											2017-02-07 05:09:09 +08:00
										 |  |  | 	signingKey := getSigningKey(cred.SecretKey, credHeader.scope.date, region) | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Get signature.
 | 
					
						
							|  |  |  | 	newSignature := getSignature(signingKey, formValues["Policy"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Verify signature.
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	if newSignature != formValues["X-Amz-Signature"] { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrSignatureDoesNotMatch | 
					
						
							| 
									
										
										
										
											2015-10-02 14:51:17 +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
										 |  |  | // 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.
 | 
					
						
							| 
									
										
										
										
											2016-09-30 06:51:00 +08:00
										 |  |  | func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region string) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Access credentials.
 | 
					
						
							|  |  |  | 	cred := serverConfig.GetCredential() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Copy request
 | 
					
						
							|  |  |  | 	req := *r | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Parse request query string.
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | 	pSignValues, err := parsePreSignV4(req.URL.Query()) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Verify if the access key id matches.
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 	if pSignValues.Credential.accessKey != cred.AccessKey { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrInvalidAccessKeyID | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-05 16:04:50 +08:00
										 |  |  | 	// Hashed payload mismatch, return content sha256 mismatch.
 | 
					
						
							| 
									
										
										
										
											2016-07-06 12:00:20 +08:00
										 |  |  | 	contentSha256 := req.URL.Query().Get("X-Amz-Content-Sha256") | 
					
						
							|  |  |  | 	if contentSha256 != "" && hashedPayload != contentSha256 { | 
					
						
							| 
									
										
										
										
											2016-07-05 16:04:50 +08:00
										 |  |  | 		return ErrContentSHA256Mismatch | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Verify if region is valid.
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | 	sRegion := pSignValues.Credential.scope.region | 
					
						
							| 
									
										
										
										
											2016-09-30 06:51:00 +08:00
										 |  |  | 	// Should validate region, only if region is set.
 | 
					
						
							|  |  |  | 	if region == "" { | 
					
						
							| 
									
										
										
										
											2016-04-02 13:45:27 +08:00
										 |  |  | 		region = sRegion | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-09-30 06:51:00 +08:00
										 |  |  | 	if !isValidRegion(sRegion, region) { | 
					
						
							|  |  |  | 		return ErrInvalidRegion | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Extract all the signed headers along with its values.
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | 	extractedSignedHeaders, errCode := extractSignedHeaders(pSignValues.SignedHeaders, req.Header) | 
					
						
							| 
									
										
										
										
											2016-08-10 00:13:15 +08:00
										 |  |  | 	if errCode != ErrNone { | 
					
						
							|  |  |  | 		return errCode | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Construct new query.
 | 
					
						
							|  |  |  | 	query := make(url.Values) | 
					
						
							| 
									
										
										
										
											2016-07-06 12:00:20 +08:00
										 |  |  | 	if contentSha256 != "" { | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							|  |  |  | 	if pSignValues.Date.After(time.Now().UTC().Add(globalMaxSkewTime)) { | 
					
						
							| 
									
										
										
										
											2016-09-10 15:38:07 +08:00
										 |  |  | 		return ErrRequestNotReadyYet | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | 	if time.Now().UTC().Sub(pSignValues.Date) > time.Duration(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 | 
					
						
							|  |  |  | 	expireSeconds := int(time.Duration(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)) | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 	query.Set("X-Amz-Credential", cred.AccessKey+"/"+getScope(t, sRegion)) | 
					
						
							| 
									
										
										
										
											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() { | 
					
						
							| 
									
										
										
										
											2016-02-07 19:37:54 +08:00
										 |  |  | 		if strings.HasPrefix(strings.ToLower(k), "x-amz") { | 
					
						
							|  |  |  | 			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") { | 
					
						
							|  |  |  | 			return ErrSignatureDoesNotMatch | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/// Verify finally if signature is same.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get canonical request.
 | 
					
						
							| 
									
										
										
										
											2016-04-07 18:04:18 +08:00
										 |  |  | 	presignedCanonicalReq := getCanonicalRequest(extractedSignedHeaders, hashedPayload, encodedQuery, req.URL.Path, req.Method, req.Host) | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2017-02-07 05:09:09 +08:00
										 |  |  | 	presignedSigningKey := getSigningKey(cred.SecretKey, pSignValues.Credential.scope.date, region) | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Get new signature.
 | 
					
						
							|  |  |  | 	newSignature := getSignature(presignedSigningKey, presignedStringToSign) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Verify signature.
 | 
					
						
							|  |  |  | 	if 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.
 | 
					
						
							| 
									
										
										
										
											2016-09-30 06:51:00 +08:00
										 |  |  | func doesSignatureMatch(hashedPayload string, r *http.Request, region string) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Access credentials.
 | 
					
						
							|  |  |  | 	cred := serverConfig.GetCredential() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// 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.
 | 
					
						
							|  |  |  | 	signV4Values, err := parseSignV4(v4Auth) | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-05 16:04:50 +08:00
										 |  |  | 	// Hashed payload mismatch, return content sha256 mismatch.
 | 
					
						
							|  |  |  | 	if hashedPayload != req.Header.Get("X-Amz-Content-Sha256") { | 
					
						
							|  |  |  | 		return ErrContentSHA256Mismatch | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-10 00:38:07 +08:00
										 |  |  | 	header := req.Header | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Signature-V4 spec excludes Content-Length from signed headers list for signature calculation.
 | 
					
						
							|  |  |  | 	// But some clients deviate from this rule. Hence we consider Content-Length for signature
 | 
					
						
							|  |  |  | 	// calculation to be compatible with such clients.
 | 
					
						
							|  |  |  | 	for _, h := range signV4Values.SignedHeaders { | 
					
						
							|  |  |  | 		if h == "content-length" { | 
					
						
							|  |  |  | 			header = cloneHeader(req.Header) | 
					
						
							| 
									
										
										
										
											2016-09-14 01:59:40 +08:00
										 |  |  | 			header.Set("content-length", strconv.FormatInt(r.ContentLength, 10)) | 
					
						
							| 
									
										
										
										
											2016-09-10 00:38:07 +08:00
										 |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Extract all the signed headers along with its values.
 | 
					
						
							| 
									
										
										
										
											2016-09-10 00:38:07 +08:00
										 |  |  | 	extractedSignedHeaders, errCode := extractSignedHeaders(signV4Values.SignedHeaders, header) | 
					
						
							| 
									
										
										
										
											2016-08-10 00:13:15 +08:00
										 |  |  | 	if errCode != ErrNone { | 
					
						
							|  |  |  | 		return errCode | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Verify if the access key id matches.
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 	if signV4Values.Credential.accessKey != cred.AccessKey { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrInvalidAccessKeyID | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-07-10 05:42:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Verify if region is valid.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	sRegion := signV4Values.Credential.scope.region | 
					
						
							| 
									
										
										
										
											2016-09-30 06:51:00 +08:00
										 |  |  | 	// Region is set to be empty, we use whatever was sent by the
 | 
					
						
							|  |  |  | 	// request and proceed further. This is a work-around to address
 | 
					
						
							|  |  |  | 	// an important problem for ListBuckets() getting signed with
 | 
					
						
							|  |  |  | 	// different regions.
 | 
					
						
							|  |  |  | 	if region == "" { | 
					
						
							|  |  |  | 		region = sRegion | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Should validate region, only if region is set.
 | 
					
						
							|  |  |  | 	if !isValidRegion(sRegion, region) { | 
					
						
							|  |  |  | 		return ErrInvalidRegion | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							|  |  |  | 	canonicalRequest := getCanonicalRequest(extractedSignedHeaders, hashedPayload, queryStr, req.URL.Path, req.Method, req.Host) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// 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.
 | 
					
						
							| 
									
										
										
										
											2017-02-07 05:09:09 +08:00
										 |  |  | 	signingKey := getSigningKey(cred.SecretKey, signV4Values.Credential.scope.date, region) | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							|  |  |  | 	if 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
										 |  |  | } |