| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  |  * Minio Cloud Storage, (C) 2015, 2016 Minio, Inc. | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package main | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | import ( | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"crypto/md5" | 
					
						
							| 
									
										
										
										
											2016-05-11 05:20:11 +08:00
										 |  |  | 	"crypto/sha256" | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	"encoding/base64" | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | 	"encoding/hex" | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-02 05:34:40 +08:00
										 |  |  | // Verify if the request http Header "x-amz-content-sha256" == "UNSIGNED-PAYLOAD"
 | 
					
						
							|  |  |  | func isRequestUnsignedPayload(r *http.Request) bool { | 
					
						
							|  |  |  | 	return r.Header.Get("x-amz-content-sha256") == unsignedPayload | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | // Verify if request has JWT.
 | 
					
						
							|  |  |  | func isRequestJWT(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 	return strings.HasPrefix(r.Header.Get("Authorization"), jwtAlgorithm) | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Verify if request has AWS Signature Version '4'.
 | 
					
						
							|  |  |  | func isRequestSignatureV4(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 	return strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm) | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | // Verify if request has AWS PreSign Version '4'.
 | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | func isRequestPresignedSignatureV4(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 	_, ok := r.URL.Query()["X-Amz-Credential"] | 
					
						
							|  |  |  | 	return ok | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Verify if request has AWS Post policy Signature Version '4'.
 | 
					
						
							|  |  |  | func isRequestPostPolicySignatureV4(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 	return strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") && r.Method == "POST" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Verify if the request has AWS Streaming Signature Version '4'. This is only valid for 'PUT' operation.
 | 
					
						
							|  |  |  | func isRequestSignStreamingV4(r *http.Request) bool { | 
					
						
							|  |  |  | 	return r.Header.Get("x-amz-content-sha256") == streamingContentSHA256 && r.Method == "PUT" | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
   "*":                             true,
   "s3:*":                          true,
   "s3:GetObject":                  true,
   "s3:ListBucket":                 true,
   "s3:PutObject":                  true,
   "s3:CreateBucket":               true,
   "s3:GetBucketLocation":          true,
   "s3:DeleteBucket":               true,
   "s3:DeleteObject":               true,
   "s3:AbortMultipartUpload":       true,
   "s3:ListBucketMultipartUploads": true,
   "s3:ListMultipartUploadParts":   true,
following conditions for "StringEquals" and "StringNotEquals"
   "s3:prefix", "s3:max-keys"
											
										 
											2016-02-04 08:46:56 +08:00
										 |  |  | // Authorization type.
 | 
					
						
							|  |  |  | type authType int | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // List of all supported auth types.
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	authTypeUnknown authType = iota | 
					
						
							|  |  |  | 	authTypeAnonymous | 
					
						
							|  |  |  | 	authTypePresigned | 
					
						
							|  |  |  | 	authTypePostPolicy | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 	authTypeStreamingSigned | 
					
						
							| 
									
										
										
											
												accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
   "*":                             true,
   "s3:*":                          true,
   "s3:GetObject":                  true,
   "s3:ListBucket":                 true,
   "s3:PutObject":                  true,
   "s3:CreateBucket":               true,
   "s3:GetBucketLocation":          true,
   "s3:DeleteBucket":               true,
   "s3:DeleteObject":               true,
   "s3:AbortMultipartUpload":       true,
   "s3:ListBucketMultipartUploads": true,
   "s3:ListMultipartUploadParts":   true,
following conditions for "StringEquals" and "StringNotEquals"
   "s3:prefix", "s3:max-keys"
											
										 
											2016-02-04 08:46:56 +08:00
										 |  |  | 	authTypeSigned | 
					
						
							|  |  |  | 	authTypeJWT | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get request authentication type.
 | 
					
						
							|  |  |  | func getRequestAuthType(r *http.Request) authType { | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 	if isRequestSignStreamingV4(r) { | 
					
						
							|  |  |  | 		return authTypeStreamingSigned | 
					
						
							|  |  |  | 	} else if isRequestSignatureV4(r) { | 
					
						
							| 
									
										
										
											
												accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
   "*":                             true,
   "s3:*":                          true,
   "s3:GetObject":                  true,
   "s3:ListBucket":                 true,
   "s3:PutObject":                  true,
   "s3:CreateBucket":               true,
   "s3:GetBucketLocation":          true,
   "s3:DeleteBucket":               true,
   "s3:DeleteObject":               true,
   "s3:AbortMultipartUpload":       true,
   "s3:ListBucketMultipartUploads": true,
   "s3:ListMultipartUploadParts":   true,
following conditions for "StringEquals" and "StringNotEquals"
   "s3:prefix", "s3:max-keys"
											
										 
											2016-02-04 08:46:56 +08:00
										 |  |  | 		return authTypeSigned | 
					
						
							|  |  |  | 	} else if isRequestPresignedSignatureV4(r) { | 
					
						
							|  |  |  | 		return authTypePresigned | 
					
						
							|  |  |  | 	} else if isRequestJWT(r) { | 
					
						
							|  |  |  | 		return authTypeJWT | 
					
						
							|  |  |  | 	} else if isRequestPostPolicySignatureV4(r) { | 
					
						
							|  |  |  | 		return authTypePostPolicy | 
					
						
							| 
									
										
										
										
											2016-03-11 09:36:48 +08:00
										 |  |  | 	} else if _, ok := r.Header["Authorization"]; !ok { | 
					
						
							|  |  |  | 		return authTypeAnonymous | 
					
						
							| 
									
										
										
											
												accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
   "*":                             true,
   "s3:*":                          true,
   "s3:GetObject":                  true,
   "s3:ListBucket":                 true,
   "s3:PutObject":                  true,
   "s3:CreateBucket":               true,
   "s3:GetBucketLocation":          true,
   "s3:DeleteBucket":               true,
   "s3:DeleteObject":               true,
   "s3:AbortMultipartUpload":       true,
   "s3:ListBucketMultipartUploads": true,
   "s3:ListMultipartUploadParts":   true,
following conditions for "StringEquals" and "StringNotEquals"
   "s3:prefix", "s3:max-keys"
											
										 
											2016-02-04 08:46:56 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return authTypeUnknown | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | // sum256 calculate sha256 sum for an input byte array
 | 
					
						
							|  |  |  | func sum256(data []byte) []byte { | 
					
						
							| 
									
										
										
										
											2016-05-11 05:20:11 +08:00
										 |  |  | 	hash := sha256.New() | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	hash.Write(data) | 
					
						
							|  |  |  | 	return hash.Sum(nil) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // sumMD5 calculate md5 sum for an input byte array
 | 
					
						
							|  |  |  | func sumMD5(data []byte) []byte { | 
					
						
							|  |  |  | 	hash := md5.New() | 
					
						
							|  |  |  | 	hash.Write(data) | 
					
						
							|  |  |  | 	return hash.Sum(nil) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | // Verify if request has valid AWS Signature Version '4'.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | func isReqAuthenticated(r *http.Request) (s3Error APIErrorCode) { | 
					
						
							|  |  |  | 	if r == nil { | 
					
						
							|  |  |  | 		return ErrInternalError | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	payload, err := ioutil.ReadAll(r.Body) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 		return ErrInternalError | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Verify Content-Md5, if payload is set.
 | 
					
						
							|  |  |  | 	if r.Header.Get("Content-Md5") != "" { | 
					
						
							|  |  |  | 		if r.Header.Get("Content-Md5") != base64.StdEncoding.EncodeToString(sumMD5(payload)) { | 
					
						
							|  |  |  | 			return ErrBadDigest | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Populate back the payload.
 | 
					
						
							|  |  |  | 	r.Body = ioutil.NopCloser(bytes.NewReader(payload)) | 
					
						
							| 
									
										
										
										
											2016-04-02 13:45:27 +08:00
										 |  |  | 	validateRegion := true // Validate region.
 | 
					
						
							| 
									
										
										
										
											2016-07-02 05:34:40 +08:00
										 |  |  | 	var sha256sum string | 
					
						
							| 
									
										
										
										
											2016-07-05 16:04:50 +08:00
										 |  |  | 	// Skips calculating sha256 on the payload on server,
 | 
					
						
							|  |  |  | 	// if client requested for it.
 | 
					
						
							|  |  |  | 	if skipContentSha256Cksum(r) { | 
					
						
							| 
									
										
										
										
											2016-07-02 05:34:40 +08:00
										 |  |  | 		sha256sum = unsignedPayload | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		sha256sum = hex.EncodeToString(sum256(payload)) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | 	if isRequestSignatureV4(r) { | 
					
						
							| 
									
										
										
										
											2016-07-02 05:34:40 +08:00
										 |  |  | 		return doesSignatureMatch(sha256sum, r, validateRegion) | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | 	} else if isRequestPresignedSignatureV4(r) { | 
					
						
							| 
									
										
										
										
											2016-07-02 05:34:40 +08:00
										 |  |  | 		return doesPresignedSignatureMatch(sha256sum, r, validateRegion) | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-10 18:24:52 +08:00
										 |  |  | 	return ErrAccessDenied | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-18 04:23:15 +08:00
										 |  |  | // checkAuth - checks for conditions satisfying the authorization of
 | 
					
						
							|  |  |  | // the incoming request. Request should be either Presigned or Signed
 | 
					
						
							|  |  |  | // in accordance with AWS S3 Signature V4 requirements. ErrAccessDenied
 | 
					
						
							|  |  |  | // is returned for unhandled auth type. Once the auth type is indentified
 | 
					
						
							|  |  |  | // request headers and body are used to calculate the signature validating
 | 
					
						
							|  |  |  | // the client signature present in request.
 | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:27 +08:00
										 |  |  | func checkAuth(r *http.Request) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 	aType := getRequestAuthType(r) | 
					
						
							|  |  |  | 	if aType != authTypePresigned && aType != authTypeSigned { | 
					
						
							| 
									
										
										
										
											2016-07-18 04:23:15 +08:00
										 |  |  | 		// For all unhandled auth types return error AccessDenied.
 | 
					
						
							|  |  |  | 		return ErrAccessDenied | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Validates the request for both Presigned and Signed.
 | 
					
						
							|  |  |  | 	return isReqAuthenticated(r) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // authHandler - handles all the incoming authorization headers and validates them if possible.
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | type authHandler struct { | 
					
						
							|  |  |  | 	handler http.Handler | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // setAuthHandler to validate authorization header for the incoming request.
 | 
					
						
							|  |  |  | func setAuthHandler(h http.Handler) http.Handler { | 
					
						
							|  |  |  | 	return authHandler{h} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | // List of all support S3 auth types.
 | 
					
						
							|  |  |  | var supportedS3AuthTypes = []authType{ | 
					
						
							|  |  |  | 	authTypeAnonymous, | 
					
						
							|  |  |  | 	authTypePresigned, | 
					
						
							|  |  |  | 	authTypeSigned, | 
					
						
							|  |  |  | 	authTypePostPolicy, | 
					
						
							|  |  |  | 	authTypeStreamingSigned, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Validate if the authType is valid and supported.
 | 
					
						
							|  |  |  | func isSupportedS3AuthType(aType authType) bool { | 
					
						
							|  |  |  | 	for _, a := range supportedS3AuthTypes { | 
					
						
							|  |  |  | 		if a == aType { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-17 10:50:36 +08:00
										 |  |  | // handler for validating incoming authorization headers.
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | func (a authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 	aType := getRequestAuthType(r) | 
					
						
							|  |  |  | 	if isSupportedS3AuthType(aType) { | 
					
						
							|  |  |  | 		// Let top level caller validate for anonymous and known signed requests.
 | 
					
						
							| 
									
										
										
										
											2016-02-17 10:50:36 +08:00
										 |  |  | 		a.handler.ServeHTTP(w, r) | 
					
						
							|  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 	} else if aType == authTypeJWT { | 
					
						
							| 
									
										
										
											
												accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
   "*":                             true,
   "s3:*":                          true,
   "s3:GetObject":                  true,
   "s3:ListBucket":                 true,
   "s3:PutObject":                  true,
   "s3:CreateBucket":               true,
   "s3:GetBucketLocation":          true,
   "s3:DeleteBucket":               true,
   "s3:DeleteObject":               true,
   "s3:AbortMultipartUpload":       true,
   "s3:ListBucketMultipartUploads": true,
   "s3:ListMultipartUploadParts":   true,
following conditions for "StringEquals" and "StringNotEquals"
   "s3:prefix", "s3:max-keys"
											
										 
											2016-02-04 08:46:56 +08:00
										 |  |  | 		// Validate Authorization header if its valid for JWT request.
 | 
					
						
							| 
									
										
										
										
											2016-02-18 18:13:52 +08:00
										 |  |  | 		if !isJWTReqAuthenticated(r) { | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 			w.WriteHeader(http.StatusUnauthorized) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
											
												accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
   "*":                             true,
   "s3:*":                          true,
   "s3:GetObject":                  true,
   "s3:ListBucket":                 true,
   "s3:PutObject":                  true,
   "s3:CreateBucket":               true,
   "s3:GetBucketLocation":          true,
   "s3:DeleteBucket":               true,
   "s3:DeleteObject":               true,
   "s3:AbortMultipartUpload":       true,
   "s3:ListBucketMultipartUploads": true,
   "s3:ListMultipartUploadParts":   true,
following conditions for "StringEquals" and "StringNotEquals"
   "s3:prefix", "s3:max-keys"
											
										 
											2016-02-04 08:46:56 +08:00
										 |  |  | 		a.handler.ServeHTTP(w, r) | 
					
						
							|  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 	writeErrorResponse(w, r, ErrSignatureVersionNotSupported, r.URL.Path) | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | } |