diff --git a/cmd/auth-handler.go b/cmd/auth-handler.go index 46dd67322..3efaa545d 100644 --- a/cmd/auth-handler.go +++ b/cmd/auth-handler.go @@ -211,8 +211,9 @@ func getClaimsFromToken(r *http.Request) (map[string]interface{}, error) { // If OPA is not set, session token should // have a policy and its mandatory, reject // requests without policy claim. - _, pok := claims.Lookup(iamPolicyClaimName()) - if !pok { + _, pokOpenID := claims.Lookup(iamPolicyClaimNameOpenID()) + _, pokSA := claims.Lookup(iamPolicyClaimNameSA()) + if !pokOpenID && !pokSA { return nil, errAuthentication } diff --git a/cmd/iam.go b/cmd/iam.go index 7212587a4..00a3a6f11 100644 --- a/cmd/iam.go +++ b/cmd/iam.go @@ -798,17 +798,18 @@ func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser, sessionPol } if len(sessionPolicy) > 16*1024 { - return auth.Credentials{}, fmt.Errorf("Session policy should not exceed 16*1024 characters") + return auth.Credentials{}, fmt.Errorf("Session policy should not exceed 16 KiB characters") } - policy, err := iampolicy.ParseConfig(bytes.NewReader([]byte(sessionPolicy))) - if err != nil { - return auth.Credentials{}, err - } - - // Version in policy must not be empty - if policy.Version == "" { - return auth.Credentials{}, fmt.Errorf("Invalid session policy version") + if len(sessionPolicy) > 0 { + policy, err := iampolicy.ParseConfig(bytes.NewReader([]byte(sessionPolicy))) + if err != nil { + return auth.Credentials{}, err + } + // Version in policy must not be empty + if policy.Version == "" { + return auth.Credentials{}, fmt.Errorf("Invalid session policy version") + } } sys.Lock() @@ -836,9 +837,14 @@ func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser, sessionPol } m := make(map[string]interface{}) - m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString([]byte(sessionPolicy)) m[parentClaim] = parentUser - m[iamPolicyClaimName()] = "embedded-policy" + + if len(sessionPolicy) > 0 { + m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString([]byte(sessionPolicy)) + m[iamPolicyClaimNameSA()] = "embedded-policy" + } else { + m[iamPolicyClaimNameSA()] = "inherited-policy" + } secret := globalActiveCred.SecretKey cred, err := auth.GetNewCredentialsWithMetadata(m, secret) @@ -1473,6 +1479,11 @@ func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parent string) b if parentInClaim != parent { return false } + } else { + // This is needed so a malicious user cannot + // use a leaked session key of another user + // to widen its privileges. + return false } // Check if the parent is allowed to perform this action, reject if not @@ -1508,12 +1519,27 @@ func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parent string) b availablePolicies[i].Statements...) } - serviceAcc := args.AccountName - args.AccountName = parent - if !combinedPolicy.IsAllowed(args) { + parentArgs := args + parentArgs.AccountName = parent + if !combinedPolicy.IsAllowed(parentArgs) { return false } - args.AccountName = serviceAcc + + saPolicyClaim, ok := args.Claims[iamPolicyClaimNameSA()] + if ok { + saPolicyClaimStr, ok := saPolicyClaim.(string) + if !ok { + // Sub policy if set, should be a string reject + // malformed/malicious requests. + return false + } + + if saPolicyClaimStr == "inherited-policy" { + // Immediately returns true since at this stage, since + // parent user is allowed to do this action. + return true + } + } // Now check if we have a sessionPolicy. spolicy, ok := args.Claims[iampolicy.SessionPolicyName] @@ -1605,7 +1631,7 @@ func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args) bool { return combinedPolicy.IsAllowed(args) } - pnameSlice, ok := args.GetPolicies(iamPolicyClaimName()) + pnameSlice, ok := args.GetPolicies(iamPolicyClaimNameOpenID()) if !ok { // When claims are set, it should have a policy claim field. return false diff --git a/cmd/sts-handlers.go b/cmd/sts-handlers.go index 0cea13b05..d3f761d60 100644 --- a/cmd/sts-handlers.go +++ b/cmd/sts-handlers.go @@ -215,7 +215,7 @@ func (sts *stsAPIHandlers) AssumeRole(w http.ResponseWriter, r *http.Request) { // This policy is the policy associated with the user // requesting for temporary credentials. The temporary // credentials will inherit the same policy requirements. - m[iamPolicyClaimName()] = policyName + m[iamPolicyClaimNameOpenID()] = policyName if len(sessionPolicyStr) > 0 { m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString([]byte(sessionPolicyStr)) @@ -351,7 +351,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithJWT(w http.ResponseWriter, r *http.Requ // be set and configured on your identity provider as part of // JWT custom claims. var policyName string - if v, ok := m[iamPolicyClaimName()]; ok { + if v, ok := m[iamPolicyClaimNameOpenID()]; ok { policyName, _ = v.(string) } diff --git a/cmd/utils.go b/cmd/utils.go index bdf886798..8de0c6625 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -620,10 +620,14 @@ func getMinioMode() string { return mode } -func iamPolicyClaimName() string { +func iamPolicyClaimNameOpenID() string { return globalOpenIDConfig.ClaimPrefix + globalOpenIDConfig.ClaimName } +func iamPolicyClaimNameSA() string { + return "sa-policy" +} + func isWORMEnabled(bucket string) bool { if isMinioMetaBucketName(bucket) { return false