CVE-2021-21362 (Go, published 2021-03-08T19:15Z)
Description: value: MinIO is an open-source high performance object storage service and it is API compatible with Amazon S3 cloud storage service. In MinIO before version RELEASE.2021-03-04T00-53-13Z it is possible to bypass a readOnly policy by creating a temporary 'mc share upload' URL. Everyone is impacted who uses MinIO multi-users. This is fixed in version RELEASE.2021-03-04T00-53-13Z. As a workaround, one can disable uploads with `Content-Type: multipart/form-data` as mentioned in the S3 API RESTObjectPOST docs by using a proxy in front of MinIO.
Severity: MEDIUM (CVSS v3 base score: 6.5)
Weakness: CWE-863 \u2014 Incorrect Authorization
The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
Repository: https://github.com/minio/minio
Files changed:
cmd/auth-handler.go
cmd/bucket-handlers.go
cmd/signature-v2.go
cmd/signature-v2_test.go
cmd/signature-v4.go
cmd/signature-v4_test.go
Commit message: fix: missing user policy enforcement in PostPolicyHandler (#11682)



Vulnerable code:
	claims, _ := getClaimsFromToken(r, getSessionToken(r))
func getClaimsFromToken(r *http.Request, token string) (map[string]interface{}, error) {
			logger.LogIf(r.Context(), err, logger.Application)
	claims, err := getClaimsFromToken(r, token)
	"path/filepath"
		claims, _ := getClaimsFromToken(r, getSessionToken(r))
	// Make sure that the URL  does not contain object name.
	if bucket != filepath.Clean(resource[1:]) {

	errCode := doesPolicySignatureMatch(formValues)

func doesPolicySignatureV2Match(formValues http.Header) APIErrorCode {
	cred := globalActiveCred
		return s3Err
		return ErrSignatureDoesNotMatch
	return ErrNone
		errCode := doesPolicySignatureV2Match(formValues)
func doesPolicySignatureMatch(formValues http.Header) APIErrorCode {
func doesPolicySignatureV4Match(formValues http.Header) APIErrorCode {
		return s3Err
		return s3Err
		return ErrSignatureDoesNotMatch
	return ErrNone
		code := doesPolicySignatureMatch(testCase.form)

Fixed code:
	claims, _ := getClaimsFromToken(getSessionToken(r))
func getClaimsFromToken(token string) (map[string]interface{}, error) {
			logger.LogIf(GlobalContext, err, logger.Application)
	claims, err := getClaimsFromToken(token)
	"crypto/subtle"
		claims, _ := getClaimsFromToken(getSessionToken(r))


	// Make sure that the URL does not contain object name.
	if bucket != path.Clean(resource[1:]) {
	cred, errCode := doesPolicySignatureMatch(formValues)
	// Once signature is validated, check if the user has
	// explicit permissions for the user.
	{
		token := formValues.Get(xhttp.AmzSecurityToken)
		if token != "" && cred.AccessKey == "" {
			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNoAccessKey), r.URL, guessIsBrowserReq(r))
			return
		}

		if cred.IsServiceAccount() && token == "" {
			token = cred.SessionToken
		}

		if subtle.ConstantTimeCompare([]byte(token), []byte(cred.SessionToken)) != 1 {
			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidToken), r.URL, guessIsBrowserReq(r))
			return
		}

		// Extract claims if any.
		claims, err := getClaimsFromToken(token)
		if err != nil {
			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
			return
		}

		if !globalIAMSys.IsAllowed(iampolicy.Args{
			AccountName:     cred.AccessKey,
			Action:          iampolicy.PutObjectAction,
			ConditionValues: getConditionValues(r, "", cred.AccessKey, claims),
			BucketName:      bucket,
			ObjectName:      object,
			IsOwner:         globalActiveCred.AccessKey == cred.AccessKey,
			Claims:          claims,
		}) {
			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL, guessIsBrowserReq(r))
			return
		}
	}

func doesPolicySignatureV2Match(formValues http.Header) (auth.Credentials, APIErrorCode) {
		return cred, s3Err
		return cred, ErrSignatureDoesNotMatch
	return cred, ErrNone
		_, errCode := doesPolicySignatureV2Match(formValues)
	"github.com/minio/minio/pkg/auth"
func doesPolicySignatureMatch(formValues http.Header) (auth.Credentials, APIErrorCode) {
func doesPolicySignatureV4Match(formValues http.Header) (auth.Credentials, APIErrorCode) {
		return auth.Credentials{}, s3Err
		return cred, s3Err
		return cred, ErrSignatureDoesNotMatch
	return cred, ErrNone
		_, code := doesPolicySignatureMatch(testCase.form)