| 
									
										
										
										
											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/>.
 | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2020-04-01 15:04:25 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2020-04-01 15:04:25 +08:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-08 19:31:43 +08:00
										 |  |  | 	jsoniter "github.com/json-iterator/go" | 
					
						
							| 
									
										
										
										
											2020-07-15 00:38:05 +08:00
										 |  |  | 	miniogopolicy "github.com/minio/minio-go/v7/pkg/policy" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/handlers" | 
					
						
							|  |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2021-05-30 12:16:42 +08:00
										 |  |  | 	"github.com/minio/pkg/bucket/policy" | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-07 03:52:56 +08:00
										 |  |  | // PolicySys - policy subsystem.
 | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | type PolicySys struct{} | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | // Get returns stored bucket policy
 | 
					
						
							|  |  |  | func (sys *PolicySys) Get(bucket string) (*policy.Policy, error) { | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 	return globalBucketMetadataSys.GetPolicyConfig(bucket) | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsAllowed - checks given policy args is allowed to continue the Rest API.
 | 
					
						
							|  |  |  | func (sys *PolicySys) IsAllowed(args policy.Args) bool { | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 	p, err := sys.Get(args.BucketName) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 		return p.IsAllowed(args) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 	// Log unhandled errors.
 | 
					
						
							|  |  |  | 	if _, ok := err.(BucketPolicyNotFound); !ok { | 
					
						
							|  |  |  | 		logger.LogIf(GlobalContext, err) | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// As policy is not available for given bucket name, returns IsOwner i.e.
 | 
					
						
							|  |  |  | 	// operation is allowed only for owner.
 | 
					
						
							|  |  |  | 	return args.IsOwner | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewPolicySys - creates new policy system.
 | 
					
						
							|  |  |  | func NewPolicySys() *PolicySys { | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 	return &PolicySys{} | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | func getConditionValues(r *http.Request, lc string, username string, claims map[string]interface{}) map[string][]string { | 
					
						
							| 
									
										
										
										
											2019-01-21 12:57:14 +08:00
										 |  |  | 	currTime := UTCNow() | 
					
						
							| 
									
										
										
										
											2020-04-01 15:04:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	principalType := "Anonymous" | 
					
						
							|  |  |  | 	if username != "" { | 
					
						
							|  |  |  | 		principalType = "User" | 
					
						
							| 
									
										
										
										
											2020-08-18 08:39:55 +08:00
										 |  |  | 		if len(claims) > 0 { | 
					
						
							|  |  |  | 			principalType = "AssumedRole" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if username == globalActiveCred.AccessKey { | 
					
						
							|  |  |  | 			principalType = "Account" | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-04-01 15:04:25 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	vid := r.Form.Get(xhttp.VersionID) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if vid == "" { | 
					
						
							|  |  |  | 		if u, err := url.Parse(r.Header.Get(xhttp.AmzCopySource)); err == nil { | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 			vid = u.Query().Get(xhttp.VersionID) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-03 00:34:15 +08:00
										 |  |  | 	authType := getRequestAuthType(r) | 
					
						
							|  |  |  | 	var signatureVersion string | 
					
						
							|  |  |  | 	switch authType { | 
					
						
							|  |  |  | 	case authTypeSignedV2, authTypePresignedV2: | 
					
						
							|  |  |  | 		signatureVersion = signV2Algorithm | 
					
						
							|  |  |  | 	case authTypeSigned, authTypePresigned, authTypeStreamingSigned, authTypePostPolicy: | 
					
						
							|  |  |  | 		signatureVersion = signV4Algorithm | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var authtype string | 
					
						
							|  |  |  | 	switch authType { | 
					
						
							|  |  |  | 	case authTypePresignedV2, authTypePresigned: | 
					
						
							|  |  |  | 		authtype = "REST-QUERY-STRING" | 
					
						
							|  |  |  | 	case authTypeSignedV2, authTypeSigned, authTypeStreamingSigned: | 
					
						
							|  |  |  | 		authtype = "REST-HEADER" | 
					
						
							|  |  |  | 	case authTypePostPolicy: | 
					
						
							|  |  |  | 		authtype = "POST" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-27 09:39:30 +08:00
										 |  |  | 	args := map[string][]string{ | 
					
						
							| 
									
										
										
										
											2021-04-03 00:34:15 +08:00
										 |  |  | 		"CurrentTime":      {currTime.Format(time.RFC3339)}, | 
					
						
							|  |  |  | 		"EpochTime":        {strconv.FormatInt(currTime.Unix(), 10)}, | 
					
						
							|  |  |  | 		"SecureTransport":  {strconv.FormatBool(r.TLS != nil)}, | 
					
						
							|  |  |  | 		"SourceIp":         {handlers.GetSourceIP(r)}, | 
					
						
							|  |  |  | 		"UserAgent":        {r.UserAgent()}, | 
					
						
							|  |  |  | 		"Referer":          {r.Referer()}, | 
					
						
							|  |  |  | 		"principaltype":    {principalType}, | 
					
						
							|  |  |  | 		"userid":           {username}, | 
					
						
							|  |  |  | 		"username":         {username}, | 
					
						
							|  |  |  | 		"versionid":        {vid}, | 
					
						
							|  |  |  | 		"signatureversion": {signatureVersion}, | 
					
						
							|  |  |  | 		"authType":         {authtype}, | 
					
						
							| 
									
										
										
										
											2018-12-27 09:39:30 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 	if lc != "" { | 
					
						
							|  |  |  | 		args["LocationConstraint"] = []string{lc} | 
					
						
							| 
									
										
										
										
											2020-04-01 15:04:25 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 	cloneHeader := r.Header.Clone() | 
					
						
							| 
									
										
										
										
											2020-04-01 15:04:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for _, objLock := range []string{ | 
					
						
							|  |  |  | 		xhttp.AmzObjectLockMode, | 
					
						
							|  |  |  | 		xhttp.AmzObjectLockLegalHold, | 
					
						
							|  |  |  | 		xhttp.AmzObjectLockRetainUntilDate, | 
					
						
							|  |  |  | 	} { | 
					
						
							|  |  |  | 		if values, ok := cloneHeader[objLock]; ok { | 
					
						
							|  |  |  | 			args[strings.TrimPrefix(objLock, "X-Amz-")] = values | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		cloneHeader.Del(objLock) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for key, values := range cloneHeader { | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 		if existingValues, found := args[key]; found { | 
					
						
							|  |  |  | 			args[key] = append(existingValues, values...) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			args[key] = values | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	cloneURLValues := make(url.Values, len(r.Form)) | 
					
						
							|  |  |  | 	for k, v := range r.Form { | 
					
						
							| 
									
										
										
										
											2020-04-01 15:04:25 +08:00
										 |  |  | 		cloneURLValues[k] = v | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, objLock := range []string{ | 
					
						
							|  |  |  | 		xhttp.AmzObjectLockMode, | 
					
						
							|  |  |  | 		xhttp.AmzObjectLockLegalHold, | 
					
						
							|  |  |  | 		xhttp.AmzObjectLockRetainUntilDate, | 
					
						
							|  |  |  | 	} { | 
					
						
							|  |  |  | 		if values, ok := cloneURLValues[objLock]; ok { | 
					
						
							|  |  |  | 			args[strings.TrimPrefix(objLock, "X-Amz-")] = values | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		cloneURLValues.Del(objLock) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for key, values := range cloneURLValues { | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 		if existingValues, found := args[key]; found { | 
					
						
							|  |  |  | 			args[key] = append(existingValues, values...) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			args[key] = values | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-16 23:59:59 +08:00
										 |  |  | 	// JWT specific values
 | 
					
						
							|  |  |  | 	for k, v := range claims { | 
					
						
							|  |  |  | 		vStr, ok := v.(string) | 
					
						
							|  |  |  | 		if ok { | 
					
						
							| 
									
										
										
										
											2020-08-18 08:39:55 +08:00
										 |  |  | 			// Special case for AD/LDAP STS users
 | 
					
						
							|  |  |  | 			if k == ldapUser { | 
					
						
							| 
									
										
										
										
											2020-09-01 14:56:22 +08:00
										 |  |  | 				args["user"] = []string{vStr} | 
					
						
							| 
									
										
										
										
											2021-07-12 09:38:52 +08:00
										 |  |  | 			} else if k == ldapUserN { | 
					
						
							| 
									
										
										
										
											2021-05-29 01:33:07 +08:00
										 |  |  | 				args["username"] = []string{vStr} | 
					
						
							| 
									
										
										
										
											2020-09-01 14:56:22 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				args[k] = []string{vStr} | 
					
						
							| 
									
										
										
										
											2020-08-18 08:39:55 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-10-16 23:59:59 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-01 15:04:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 	return args | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // PolicyToBucketAccessPolicy converts a MinIO policy into a minio-go policy data structure.
 | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | func PolicyToBucketAccessPolicy(bucketPolicy *policy.Policy) (*miniogopolicy.BucketAccessPolicy, error) { | 
					
						
							|  |  |  | 	// Return empty BucketAccessPolicy for empty bucket policy.
 | 
					
						
							|  |  |  | 	if bucketPolicy == nil { | 
					
						
							|  |  |  | 		return &miniogopolicy.BucketAccessPolicy{Version: policy.DefaultVersion}, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data, err := json.Marshal(bucketPolicy) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// This should not happen because bucketPolicy is valid to convert to JSON data.
 | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var policyInfo miniogopolicy.BucketAccessPolicy | 
					
						
							| 
									
										
										
										
											2020-01-08 19:31:43 +08:00
										 |  |  | 	var json = jsoniter.ConfigCompatibleWithStandardLibrary | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 	if err = json.Unmarshal(data, &policyInfo); err != nil { | 
					
						
							|  |  |  | 		// This should not happen because data is valid to JSON data.
 | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &policyInfo, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // BucketAccessPolicyToPolicy - converts minio-go/policy.BucketAccessPolicy to policy.Policy.
 | 
					
						
							|  |  |  | func BucketAccessPolicyToPolicy(policyInfo *miniogopolicy.BucketAccessPolicy) (*policy.Policy, error) { | 
					
						
							|  |  |  | 	data, err := json.Marshal(policyInfo) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// This should not happen because policyInfo is valid to convert to JSON data.
 | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var bucketPolicy policy.Policy | 
					
						
							| 
									
										
										
										
											2020-01-08 19:31:43 +08:00
										 |  |  | 	var json = jsoniter.ConfigCompatibleWithStandardLibrary | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 	if err = json.Unmarshal(data, &bucketPolicy); err != nil { | 
					
						
							|  |  |  | 		// This should not happen because data is valid to JSON data.
 | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &bucketPolicy, nil | 
					
						
							|  |  |  | } |