| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-15 00:38:05 +08:00
										 |  |  | 	"github.com/minio/minio-go/v7/pkg/s3utils" | 
					
						
							| 
									
										
										
										
											2020-12-02 18:02:49 +08:00
										 |  |  | 	"github.com/minio/minio-go/v7/pkg/set" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/auth" | 
					
						
							| 
									
										
										
										
											2022-05-27 21:00:19 +08:00
										 |  |  | 	"github.com/minio/minio/internal/hash/sha256" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							| 
									
										
										
										
											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 =
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-08-27 03:52:29 +08:00
										 |  |  | //	<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 { | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  | 	rawQuery := strings.ReplaceAll(queryStr, "+", "%20") | 
					
						
							| 
									
										
										
										
											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", | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 	}, SlashSeparator) | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	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" | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  | 	stringToSign += scope + "\n" | 
					
						
							| 
									
										
										
										
											2016-02-11 08:40:09 +08:00
										 |  |  | 	canonicalRequestBytes := sha256.Sum256([]byte(canonicalRequest)) | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  | 	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.
 | 
					
						
							| 
									
										
										
										
											2021-03-04 00:47:08 +08:00
										 |  |  | func doesPolicySignatureMatch(formValues http.Header) (auth.Credentials, 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
 | 
					
						
							| 
									
										
										
										
											2022-08-27 03:52:29 +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.
 | 
					
						
							| 
									
										
										
										
											2021-03-04 00:47:08 +08:00
										 |  |  | func doesPolicySignatureV4Match(formValues http.Header) (auth.Credentials, APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Server region.
 | 
					
						
							| 
									
										
										
										
											2021-11-26 05:06:25 +08:00
										 |  |  | 	region := globalSite.Region | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Parse credential tag.
 | 
					
						
							| 
									
										
										
										
											2020-08-24 13:06:22 +08:00
										 |  |  | 	credHeader, s3Err := parseCredentialHeader("Credential="+formValues.Get(xhttp.AmzCredential), region, serviceS3) | 
					
						
							|  |  |  | 	if s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-03-04 00:47:08 +08:00
										 |  |  | 		return auth.Credentials{}, s3Err | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-13 09:07:08 +08:00
										 |  |  | 	r := &http.Request{Header: formValues} | 
					
						
							|  |  |  | 	cred, _, s3Err := checkKeyValid(r, credHeader.accessKey) | 
					
						
							| 
									
										
										
										
											2018-11-07 22:40:03 +08:00
										 |  |  | 	if s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-03-04 00:47:08 +08:00
										 |  |  | 		return cred, 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.
 | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	if !compareSignatureV4(newSignature, formValues.Get(xhttp.AmzSignature)) { | 
					
						
							| 
									
										
										
										
											2021-03-04 00:47:08 +08:00
										 |  |  | 		return cred, ErrSignatureDoesNotMatch | 
					
						
							| 
									
										
										
										
											2015-10-02 14:51:17 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Success.
 | 
					
						
							| 
									
										
										
										
											2021-03-04 00:47:08 +08:00
										 |  |  | 	return cred, ErrNone | 
					
						
							| 
									
										
										
										
											2015-10-01 13:59:52 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | // doesPresignedSignatureMatch - Verify query headers with presigned signature
 | 
					
						
							| 
									
										
										
										
											2022-08-27 03:52:29 +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.
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	pSignValues, err := parsePreSignV4(req.Form, 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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-13 09:07:08 +08:00
										 |  |  | 	cred, _, s3Err := checkKeyValid(r, pSignValues.Credential.accessKey) | 
					
						
							| 
									
										
										
										
											2018-11-07 22:40:03 +08:00
										 |  |  | 	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-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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-19 09:04:50 +08:00
										 |  |  | 	// Construct new query.
 | 
					
						
							|  |  |  | 	query := make(url.Values) | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	clntHashedPayload := req.Form.Get(xhttp.AmzContentSha256) | 
					
						
							| 
									
										
										
										
											2020-01-19 09:04:50 +08:00
										 |  |  | 	if clntHashedPayload != "" { | 
					
						
							|  |  |  | 		query.Set(xhttp.AmzContentSha256, hashedPayload) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	token := req.Form.Get(xhttp.AmzSecurityToken) | 
					
						
							| 
									
										
										
										
											2020-01-19 09:04:50 +08:00
										 |  |  | 	if token != "" { | 
					
						
							|  |  |  | 		query.Set(xhttp.AmzSecurityToken, cred.SessionToken) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	query.Set(xhttp.AmzAlgorithm, signV4Algorithm) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-18 18:13:52 +08:00
										 |  |  | 	// Construct the query.
 | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	query.Set(xhttp.AmzDate, t.Format(iso8601Format)) | 
					
						
							|  |  |  | 	query.Set(xhttp.AmzExpires, strconv.Itoa(expireSeconds)) | 
					
						
							|  |  |  | 	query.Set(xhttp.AmzSignedHeaders, getSignedHeaders(extractedSignedHeaders)) | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 	query.Set(xhttp.AmzCredential, cred.AccessKey+SlashSeparator+pSignValues.Credential.getScope()) | 
					
						
							| 
									
										
										
										
											2016-02-07 19:37:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 18:02:49 +08:00
										 |  |  | 	defaultSigParams := set.CreateStringSet( | 
					
						
							|  |  |  | 		xhttp.AmzContentSha256, | 
					
						
							|  |  |  | 		xhttp.AmzSecurityToken, | 
					
						
							|  |  |  | 		xhttp.AmzAlgorithm, | 
					
						
							|  |  |  | 		xhttp.AmzDate, | 
					
						
							|  |  |  | 		xhttp.AmzExpires, | 
					
						
							|  |  |  | 		xhttp.AmzSignedHeaders, | 
					
						
							|  |  |  | 		xhttp.AmzCredential, | 
					
						
							|  |  |  | 		xhttp.AmzSignature, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Add missing query parameters if any provided in the request URL
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	for k, v := range req.Form { | 
					
						
							| 
									
										
										
										
											2020-12-02 18:02:49 +08:00
										 |  |  | 		if !defaultSigParams.Contains(k) { | 
					
						
							|  |  |  | 			query[k] = v | 
					
						
							| 
									
										
										
										
											2016-02-07 19:37:54 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	if req.Form.Get(xhttp.AmzDate) != query.Get(xhttp.AmzDate) { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrSignatureDoesNotMatch | 
					
						
							| 
									
										
										
										
											2016-01-29 03:55:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Verify if expires query is same.
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	if req.Form.Get(xhttp.AmzExpires) != query.Get(xhttp.AmzExpires) { | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	if req.Form.Get(xhttp.AmzSignedHeaders) != query.Get(xhttp.AmzSignedHeaders) { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return ErrSignatureDoesNotMatch | 
					
						
							| 
									
										
										
										
											2016-01-29 03:55:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Verify if credential query is same.
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	if req.Form.Get(xhttp.AmzCredential) != query.Get(xhttp.AmzCredential) { | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2020-01-19 09:04:50 +08:00
										 |  |  | 	if clntHashedPayload != "" && clntHashedPayload != query.Get(xhttp.AmzContentSha256) { | 
					
						
							|  |  |  | 		return ErrContentSHA256Mismatch | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Verify if security token is correct.
 | 
					
						
							|  |  |  | 	if token != "" && subtle.ConstantTimeCompare([]byte(token), []byte(cred.SessionToken)) != 1 { | 
					
						
							|  |  |  | 		return ErrInvalidToken | 
					
						
							| 
									
										
										
										
											2016-04-07 18:04:18 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  | 	// Verify finally if signature is same.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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.
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	if !compareSignatureV4(req.Form.Get(xhttp.AmzSignature), 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
 | 
					
						
							| 
									
										
										
										
											2022-08-27 03:52:29 +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.
 | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	v4Auth := req.Header.Get(xhttp.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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-13 09:07:08 +08:00
										 |  |  | 	cred, _, s3Err := checkKeyValid(r, signV4Values.Credential.accessKey) | 
					
						
							| 
									
										
										
										
											2018-11-07 22:40:03 +08:00
										 |  |  | 	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 | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	if date = req.Header.Get(xhttp.AmzDate); date == "" { | 
					
						
							|  |  |  | 		if date = r.Header.Get(xhttp.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.
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	queryStr := req.Form.Encode() | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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
										 |  |  | } |