mirror of https://github.com/minio/minio.git
bucketPolicy: Do not use regexes, just do prefix matches. (#1497)
AWS arn supports wildcards and this is flat namespace, simple prefix matching is fine. Fixes #1481 Fixes #1482
This commit is contained in:
parent
ca097de96c
commit
ba5805e60a
|
@ -75,41 +75,41 @@ func bucketPolicyActionMatch(action string, statement policyStatement) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if given resource matches with policy statement.
|
// Match function matches wild cards in 'pattern' for resource.
|
||||||
func bucketPolicyResourceMatch(resource string, statement policyStatement) bool {
|
func resourceMatch(pattern, resource string) bool {
|
||||||
|
if pattern == "" {
|
||||||
match := func(pattern, subj string) bool {
|
return resource == pattern
|
||||||
if pattern == "" {
|
}
|
||||||
return subj == pattern
|
if pattern == "*" {
|
||||||
}
|
return true
|
||||||
if pattern == "*" {
|
}
|
||||||
return true
|
parts := strings.Split(pattern, "*")
|
||||||
}
|
if len(parts) == 1 {
|
||||||
parts := strings.Split(pattern, "*")
|
return resource == pattern
|
||||||
if len(parts) == 1 {
|
}
|
||||||
return subj == pattern
|
tGlob := strings.HasSuffix(pattern, "*")
|
||||||
}
|
end := len(parts) - 1
|
||||||
tGlob := strings.HasSuffix(pattern, "*")
|
if !strings.HasPrefix(resource, parts[0]) {
|
||||||
end := len(parts) - 1
|
return false
|
||||||
if !strings.HasPrefix(subj, parts[0]) {
|
}
|
||||||
|
for i := 1; i < end; i++ {
|
||||||
|
if !strings.Contains(resource, parts[i]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i := 1; i < end; i++ {
|
idx := strings.Index(resource, parts[i]) + len(parts[i])
|
||||||
if !strings.Contains(subj, parts[i]) {
|
resource = resource[idx:]
|
||||||
return false
|
|
||||||
}
|
|
||||||
idx := strings.Index(subj, parts[i]) + len(parts[i])
|
|
||||||
subj = subj[idx:]
|
|
||||||
}
|
|
||||||
return tGlob || strings.HasSuffix(subj, parts[end])
|
|
||||||
}
|
}
|
||||||
|
return tGlob || strings.HasSuffix(resource, parts[end])
|
||||||
|
}
|
||||||
|
|
||||||
for _, presource := range statement.Resources {
|
// Verify if given resource matches with policy statement.
|
||||||
|
func bucketPolicyResourceMatch(resource string, statement policyStatement) bool {
|
||||||
|
for _, resourcep := range statement.Resources {
|
||||||
// the resource rule for object could contain "*" wild card.
|
// the resource rule for object could contain "*" wild card.
|
||||||
// the requested object can be given access based on the already set bucket policy if
|
// the requested object can be given access based on the already set bucket policy if
|
||||||
// the match is successful.
|
// the match is successful.
|
||||||
// More info: http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-arn-format.html .
|
// More info: http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-arn-format.html .
|
||||||
if matched := match(presource, resource); !matched {
|
if matched := resourceMatch(resourcep, resource); !matched {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -200,6 +200,14 @@ var invalidPrefixActions = map[string]struct{}{
|
||||||
// Add actions which do not honor prefixes.
|
// Add actions which do not honor prefixes.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resourcePrefix - provides the prefix removing any wildcards.
|
||||||
|
func resourcePrefix(resource string) string {
|
||||||
|
if strings.HasSuffix(resource, "*") {
|
||||||
|
resource = strings.TrimSuffix(resource, "*")
|
||||||
|
}
|
||||||
|
return path.Clean(resource)
|
||||||
|
}
|
||||||
|
|
||||||
// checkBucketPolicyResources validates Resources in unmarshalled bucket policy structure.
|
// checkBucketPolicyResources validates Resources in unmarshalled bucket policy structure.
|
||||||
// First valation of Resources done for given set of Actions.
|
// First valation of Resources done for given set of Actions.
|
||||||
// Later its validated for recursive Resources.
|
// Later its validated for recursive Resources.
|
||||||
|
@ -232,7 +240,7 @@ func checkBucketPolicyResources(bucket string, bucketPolicy BucketPolicy) APIErr
|
||||||
|
|
||||||
var resources []string
|
var resources []string
|
||||||
for resource := range resourceMap {
|
for resource := range resourceMap {
|
||||||
resources = append(resources, resource)
|
resources = append(resources, resourcePrefix(resource))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort strings as shorter first.
|
// Sort strings as shorter first.
|
||||||
|
@ -241,12 +249,12 @@ func checkBucketPolicyResources(bucket string, bucketPolicy BucketPolicy) APIErr
|
||||||
for len(resources) > 1 {
|
for len(resources) > 1 {
|
||||||
var resource string
|
var resource string
|
||||||
resource, resources = resources[0], resources[1:]
|
resource, resources = resources[0], resources[1:]
|
||||||
resourceRegex := regexp.MustCompile(resource)
|
|
||||||
// Loop through all resources, if one of them matches with
|
// Loop through all resources, if one of them matches with
|
||||||
// previous shorter one, it means we have detected
|
// previous shorter one, it means we have detected
|
||||||
// nesting. Reject such rules.
|
// nesting. Reject such rules.
|
||||||
for _, otherResource := range resources {
|
for _, otherResource := range resources {
|
||||||
if resourceRegex.MatchString(otherResource) {
|
// Common prefix reject such rules.
|
||||||
|
if strings.HasPrefix(otherResource, resource) {
|
||||||
return ErrMalformedPolicy
|
return ErrMalformedPolicy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -453,31 +453,42 @@ func TestCheckBucketPolicyResources(t *testing.T) {
|
||||||
return statements
|
return statements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// constructing policy statement with lexically close characters.
|
||||||
|
// should not result in ErrMalformedPolicy
|
||||||
|
setResourceLexical := func(statements []policyStatement) []policyStatement {
|
||||||
|
statements[0].Resources = []string{"arn:aws:s3:::minio-bucket/op*", "arn:aws:s3:::minio-bucket/oo*"}
|
||||||
|
return statements
|
||||||
|
}
|
||||||
|
|
||||||
// List of BucketPolicy used for tests.
|
// List of BucketPolicy used for tests.
|
||||||
bucketAccessPolicies := []BucketPolicy{
|
bucketAccessPolicies := []BucketPolicy{
|
||||||
// BucketPolicy - 0.
|
// BucketPolicy - 1.
|
||||||
// Contains valid read only policy statement.
|
// Contains valid read only policy statement.
|
||||||
{Version: "1.0", Statements: setReadOnlyStatement("minio-bucket", "")},
|
{Version: "1.0", Statements: setReadOnlyStatement("minio-bucket", "")},
|
||||||
// BucketPolicy - 1.
|
// BucketPolicy - 2.
|
||||||
// Contains valid read-write only policy statement.
|
// Contains valid read-write only policy statement.
|
||||||
{Version: "1.0", Statements: setReadWriteStatement("minio-bucket", "Asia/")},
|
{Version: "1.0", Statements: setReadWriteStatement("minio-bucket", "Asia/")},
|
||||||
// BucketPolicy - 2.
|
// BucketPolicy - 3.
|
||||||
// Contains valid write only policy statement.
|
// Contains valid write only policy statement.
|
||||||
{Version: "1.0", Statements: setWriteOnlyStatement("minio-bucket", "Asia/India/")},
|
{Version: "1.0", Statements: setWriteOnlyStatement("minio-bucket", "Asia/India/")},
|
||||||
// BucketPolicy - 3.
|
// BucketPolicy - 4.
|
||||||
// Contains invalidPrefixActions.
|
// Contains invalidPrefixActions.
|
||||||
// Since resourcePrefix is not to the bucket-name, it return ErrMalformedPolicy.
|
// Since resourcePrefix is not to the bucket-name, it return ErrMalformedPolicy.
|
||||||
{Version: "1.0", Statements: setReadOnlyStatement("minio-bucket-fail", "Asia/India/")},
|
{Version: "1.0", Statements: setReadOnlyStatement("minio-bucket-fail", "Asia/India/")},
|
||||||
// BucketPolicy - 4.
|
// BucketPolicy - 5.
|
||||||
// constructing policy statement without invalidPrefixActions (check bucket-policy-parser.go).
|
// constructing policy statement without invalidPrefixActions (check bucket-policy-parser.go).
|
||||||
// but bucket part of the resource is not equal to the bucket name.
|
// but bucket part of the resource is not equal to the bucket name.
|
||||||
// this results in return of ErrMalformedPolicy.
|
// this results in return of ErrMalformedPolicy.
|
||||||
{Version: "1.0", Statements: setValidPrefixActions(setWriteOnlyStatement("minio-bucket-fail", "Asia/India/"))},
|
{Version: "1.0", Statements: setValidPrefixActions(setWriteOnlyStatement("minio-bucket-fail", "Asia/India/"))},
|
||||||
// BucketPolicy - 5.
|
// BucketPolicy - 6.
|
||||||
// constructing policy statement without invalidPrefixActions (check bucket-policy-parser.go).
|
|
||||||
// contructing policy statement with recursive resources.
|
// contructing policy statement with recursive resources.
|
||||||
// should result in ErrMalformedPolicy
|
// should result in ErrMalformedPolicy
|
||||||
{Version: "1.0", Statements: setRecurseResource(setValidPrefixActions(setWriteOnlyStatement("minio-bucket", "")))},
|
{Version: "1.0", Statements: setRecurseResource(setValidPrefixActions(setWriteOnlyStatement("minio-bucket", "")))},
|
||||||
|
// BucketPolciy - 7.
|
||||||
|
// constructing policy statment with non recursive but
|
||||||
|
// lexically close resources.
|
||||||
|
// should result in ErrNone.
|
||||||
|
{Version: "1.0", Statements: setResourceLexical(setValidPrefixActions(setWriteOnlyStatement("minio-bucket", "oo")))},
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
@ -505,6 +516,11 @@ func TestCheckBucketPolicyResources(t *testing.T) {
|
||||||
// contructing policy statement with recursive resources.
|
// contructing policy statement with recursive resources.
|
||||||
// should result in ErrMalformedPolicy.
|
// should result in ErrMalformedPolicy.
|
||||||
{bucketAccessPolicies[5], ErrMalformedPolicy, false},
|
{bucketAccessPolicies[5], ErrMalformedPolicy, false},
|
||||||
|
// Test case - 7.
|
||||||
|
// constructing policy statement with lexically close
|
||||||
|
// characters.
|
||||||
|
// should result in ErrNone.
|
||||||
|
{bucketAccessPolicies[6], ErrNone, true},
|
||||||
}
|
}
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
apiErrCode := checkBucketPolicyResources("minio-bucket", testCase.inputPolicy)
|
apiErrCode := checkBucketPolicyResources("minio-bucket", testCase.inputPolicy)
|
||||||
|
|
Loading…
Reference in New Issue