github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/auth-handler.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"bytes"
    22  	"context"
    23  	"crypto/subtle"
    24  	"encoding/base64"
    25  	"encoding/hex"
    26  	"errors"
    27  	"io"
    28  	"mime"
    29  	"net/http"
    30  	"net/url"
    31  	"strconv"
    32  	"strings"
    33  	"sync/atomic"
    34  	"time"
    35  
    36  	"github.com/minio/minio/internal/auth"
    37  	objectlock "github.com/minio/minio/internal/bucket/object/lock"
    38  	"github.com/minio/minio/internal/etag"
    39  	"github.com/minio/minio/internal/hash"
    40  	xhttp "github.com/minio/minio/internal/http"
    41  	xjwt "github.com/minio/minio/internal/jwt"
    42  	"github.com/minio/minio/internal/logger"
    43  	"github.com/minio/minio/internal/mcontext"
    44  	"github.com/minio/pkg/v2/policy"
    45  )
    46  
    47  // Verify if request has JWT.
    48  func isRequestJWT(r *http.Request) bool {
    49  	return strings.HasPrefix(r.Header.Get(xhttp.Authorization), jwtAlgorithm)
    50  }
    51  
    52  // Verify if request has AWS Signature Version '4'.
    53  func isRequestSignatureV4(r *http.Request) bool {
    54  	return strings.HasPrefix(r.Header.Get(xhttp.Authorization), signV4Algorithm)
    55  }
    56  
    57  // Verify if request has AWS Signature Version '2'.
    58  func isRequestSignatureV2(r *http.Request) bool {
    59  	return (!strings.HasPrefix(r.Header.Get(xhttp.Authorization), signV4Algorithm) &&
    60  		strings.HasPrefix(r.Header.Get(xhttp.Authorization), signV2Algorithm))
    61  }
    62  
    63  // Verify if request has AWS PreSign Version '4'.
    64  func isRequestPresignedSignatureV4(r *http.Request) bool {
    65  	_, ok := r.Form[xhttp.AmzCredential]
    66  	return ok
    67  }
    68  
    69  // Verify request has AWS PreSign Version '2'.
    70  func isRequestPresignedSignatureV2(r *http.Request) bool {
    71  	_, ok := r.Form[xhttp.AmzAccessKeyID]
    72  	return ok
    73  }
    74  
    75  // Verify if request has AWS Post policy Signature Version '4'.
    76  func isRequestPostPolicySignatureV4(r *http.Request) bool {
    77  	mediaType, _, err := mime.ParseMediaType(r.Header.Get(xhttp.ContentType))
    78  	if err != nil {
    79  		return false
    80  	}
    81  	return mediaType == "multipart/form-data" && r.Method == http.MethodPost
    82  }
    83  
    84  // Verify if the request has AWS Streaming Signature Version '4'. This is only valid for 'PUT' operation.
    85  func isRequestSignStreamingV4(r *http.Request) bool {
    86  	return r.Header.Get(xhttp.AmzContentSha256) == streamingContentSHA256 &&
    87  		r.Method == http.MethodPut
    88  }
    89  
    90  // Verify if the request has AWS Streaming Signature Version '4'. This is only valid for 'PUT' operation.
    91  func isRequestSignStreamingTrailerV4(r *http.Request) bool {
    92  	return r.Header.Get(xhttp.AmzContentSha256) == streamingContentSHA256Trailer &&
    93  		r.Method == http.MethodPut
    94  }
    95  
    96  // Verify if the request has AWS Streaming Signature Version '4', with unsigned content and trailer.
    97  func isRequestUnsignedTrailerV4(r *http.Request) bool {
    98  	return r.Header.Get(xhttp.AmzContentSha256) == unsignedPayloadTrailer &&
    99  		r.Method == http.MethodPut && strings.Contains(r.Header.Get(xhttp.ContentEncoding), streamingContentEncoding)
   100  }
   101  
   102  // Authorization type.
   103  //
   104  //go:generate stringer -type=authType -trimprefix=authType $GOFILE
   105  type authType int
   106  
   107  // List of all supported auth types.
   108  const (
   109  	authTypeUnknown authType = iota
   110  	authTypeAnonymous
   111  	authTypePresigned
   112  	authTypePresignedV2
   113  	authTypePostPolicy
   114  	authTypeStreamingSigned
   115  	authTypeSigned
   116  	authTypeSignedV2
   117  	authTypeJWT
   118  	authTypeSTS
   119  	authTypeStreamingSignedTrailer
   120  	authTypeStreamingUnsignedTrailer
   121  )
   122  
   123  // Get request authentication type.
   124  func getRequestAuthType(r *http.Request) (at authType) {
   125  	if r.URL != nil {
   126  		var err error
   127  		r.Form, err = url.ParseQuery(r.URL.RawQuery)
   128  		if err != nil {
   129  			logger.LogIf(r.Context(), err)
   130  			return authTypeUnknown
   131  		}
   132  	}
   133  	if isRequestSignatureV2(r) {
   134  		return authTypeSignedV2
   135  	} else if isRequestPresignedSignatureV2(r) {
   136  		return authTypePresignedV2
   137  	} else if isRequestSignStreamingV4(r) {
   138  		return authTypeStreamingSigned
   139  	} else if isRequestSignStreamingTrailerV4(r) {
   140  		return authTypeStreamingSignedTrailer
   141  	} else if isRequestUnsignedTrailerV4(r) {
   142  		return authTypeStreamingUnsignedTrailer
   143  	} else if isRequestSignatureV4(r) {
   144  		return authTypeSigned
   145  	} else if isRequestPresignedSignatureV4(r) {
   146  		return authTypePresigned
   147  	} else if isRequestJWT(r) {
   148  		return authTypeJWT
   149  	} else if isRequestPostPolicySignatureV4(r) {
   150  		return authTypePostPolicy
   151  	} else if _, ok := r.Form[xhttp.Action]; ok {
   152  		return authTypeSTS
   153  	} else if _, ok := r.Header[xhttp.Authorization]; !ok {
   154  		return authTypeAnonymous
   155  	}
   156  	return authTypeUnknown
   157  }
   158  
   159  func validateAdminSignature(ctx context.Context, r *http.Request, region string) (auth.Credentials, bool, APIErrorCode) {
   160  	var cred auth.Credentials
   161  	var owner bool
   162  	s3Err := ErrAccessDenied
   163  	if _, ok := r.Header[xhttp.AmzContentSha256]; ok &&
   164  		getRequestAuthType(r) == authTypeSigned {
   165  
   166  		// Get credential information from the request.
   167  		cred, owner, s3Err = getReqAccessKeyV4(r, region, serviceS3)
   168  		if s3Err != ErrNone {
   169  			return cred, owner, s3Err
   170  		}
   171  
   172  		// we only support V4 (no presign) with auth body
   173  		s3Err = isReqAuthenticated(ctx, r, region, serviceS3)
   174  	}
   175  	if s3Err != ErrNone {
   176  		return cred, owner, s3Err
   177  	}
   178  
   179  	logger.GetReqInfo(ctx).Cred = cred
   180  	logger.GetReqInfo(ctx).Owner = owner
   181  	logger.GetReqInfo(ctx).Region = globalSite.Region
   182  
   183  	return cred, owner, ErrNone
   184  }
   185  
   186  // checkAdminRequestAuth checks for authentication and authorization for the incoming
   187  // request. It only accepts V2 and V4 requests. Presigned, JWT and anonymous requests
   188  // are automatically rejected.
   189  func checkAdminRequestAuth(ctx context.Context, r *http.Request, action policy.AdminAction, region string) (auth.Credentials, APIErrorCode) {
   190  	cred, owner, s3Err := validateAdminSignature(ctx, r, region)
   191  	if s3Err != ErrNone {
   192  		return cred, s3Err
   193  	}
   194  	if globalIAMSys.IsAllowed(policy.Args{
   195  		AccountName:     cred.AccessKey,
   196  		Groups:          cred.Groups,
   197  		Action:          policy.Action(action),
   198  		ConditionValues: getConditionValues(r, "", cred),
   199  		IsOwner:         owner,
   200  		Claims:          cred.Claims,
   201  	}) {
   202  		// Request is allowed return the appropriate access key.
   203  		return cred, ErrNone
   204  	}
   205  
   206  	return cred, ErrAccessDenied
   207  }
   208  
   209  // Fetch the security token set by the client.
   210  func getSessionToken(r *http.Request) (token string) {
   211  	token = r.Header.Get(xhttp.AmzSecurityToken)
   212  	if token != "" {
   213  		return token
   214  	}
   215  	return r.Form.Get(xhttp.AmzSecurityToken)
   216  }
   217  
   218  // Fetch claims in the security token returned by the client, doesn't return
   219  // errors - upon errors the returned claims map will be empty.
   220  func mustGetClaimsFromToken(r *http.Request) map[string]interface{} {
   221  	claims, _ := getClaimsFromToken(getSessionToken(r))
   222  	return claims
   223  }
   224  
   225  func getClaimsFromTokenWithSecret(token, secret string) (map[string]interface{}, error) {
   226  	// JWT token for x-amz-security-token is signed with admin
   227  	// secret key, temporary credentials become invalid if
   228  	// server admin credentials change. This is done to ensure
   229  	// that clients cannot decode the token using the temp
   230  	// secret keys and generate an entirely new claim by essentially
   231  	// hijacking the policies. We need to make sure that this is
   232  	// based on admin credential such that token cannot be decoded
   233  	// on the client side and is treated like an opaque value.
   234  	claims, err := auth.ExtractClaims(token, secret)
   235  	if err != nil {
   236  		if subtle.ConstantTimeCompare([]byte(secret), []byte(globalActiveCred.SecretKey)) == 1 {
   237  			return nil, errAuthentication
   238  		}
   239  		claims, err = auth.ExtractClaims(token, globalActiveCred.SecretKey)
   240  		if err != nil {
   241  			return nil, errAuthentication
   242  		}
   243  	}
   244  
   245  	// If AuthZPlugin is set, return without any further checks.
   246  	if newGlobalAuthZPluginFn() != nil {
   247  		return claims.Map(), nil
   248  	}
   249  
   250  	// Check if a session policy is set. If so, decode it here.
   251  	sp, spok := claims.Lookup(policy.SessionPolicyName)
   252  	if spok {
   253  		// Looks like subpolicy is set and is a string, if set then its
   254  		// base64 encoded, decode it. Decoding fails reject such
   255  		// requests.
   256  		spBytes, err := base64.StdEncoding.DecodeString(sp)
   257  		if err != nil {
   258  			// Base64 decoding fails, we should log to indicate
   259  			// something is malforming the request sent by client.
   260  			logger.LogIf(GlobalContext, err, logger.ErrorKind)
   261  			return nil, errAuthentication
   262  		}
   263  		claims.MapClaims[sessionPolicyNameExtracted] = string(spBytes)
   264  	}
   265  
   266  	return claims.Map(), nil
   267  }
   268  
   269  // Fetch claims in the security token returned by the client.
   270  func getClaimsFromToken(token string) (map[string]interface{}, error) {
   271  	return getClaimsFromTokenWithSecret(token, globalActiveCred.SecretKey)
   272  }
   273  
   274  // Fetch claims in the security token returned by the client and validate the token.
   275  func checkClaimsFromToken(r *http.Request, cred auth.Credentials) (map[string]interface{}, APIErrorCode) {
   276  	token := getSessionToken(r)
   277  	if token != "" && cred.AccessKey == "" {
   278  		// x-amz-security-token is not allowed for anonymous access.
   279  		return nil, ErrNoAccessKey
   280  	}
   281  
   282  	if token == "" && cred.IsTemp() && !cred.IsServiceAccount() {
   283  		// Temporary credentials should always have x-amz-security-token
   284  		return nil, ErrInvalidToken
   285  	}
   286  
   287  	if token != "" && !cred.IsTemp() {
   288  		// x-amz-security-token should not present for static credentials.
   289  		return nil, ErrInvalidToken
   290  	}
   291  
   292  	if !cred.IsServiceAccount() && cred.IsTemp() && subtle.ConstantTimeCompare([]byte(token), []byte(cred.SessionToken)) != 1 {
   293  		// validate token for temporary credentials only.
   294  		return nil, ErrInvalidToken
   295  	}
   296  
   297  	// Expired credentials must return error right away.
   298  	if cred.IsTemp() && cred.IsExpired() {
   299  		return nil, toAPIErrorCode(r.Context(), errInvalidAccessKeyID)
   300  	}
   301  	secret := globalActiveCred.SecretKey
   302  	if globalSiteReplicationSys.isEnabled() && cred.AccessKey != siteReplicatorSvcAcc {
   303  		nsecret, err := getTokenSigningKey()
   304  		if err != nil {
   305  			return nil, toAPIErrorCode(r.Context(), err)
   306  		}
   307  		// sign root's temporary accounts also with site replicator creds
   308  		if cred.ParentUser != globalActiveCred.AccessKey || cred.IsTemp() {
   309  			secret = nsecret
   310  		}
   311  	}
   312  	if cred.IsServiceAccount() {
   313  		token = cred.SessionToken
   314  		secret = cred.SecretKey
   315  	}
   316  
   317  	if token != "" {
   318  		claims, err := getClaimsFromTokenWithSecret(token, secret)
   319  		if err != nil {
   320  			return nil, toAPIErrorCode(r.Context(), err)
   321  		}
   322  		return claims, ErrNone
   323  	}
   324  
   325  	claims := xjwt.NewMapClaims()
   326  	return claims.Map(), ErrNone
   327  }
   328  
   329  // Check request auth type verifies the incoming http request
   330  //   - validates the request signature
   331  //   - validates the policy action if anonymous tests bucket policies if any,
   332  //     for authenticated requests validates IAM policies.
   333  //
   334  // returns APIErrorCode if any to be replied to the client.
   335  func checkRequestAuthType(ctx context.Context, r *http.Request, action policy.Action, bucketName, objectName string) (s3Err APIErrorCode) {
   336  	logger.GetReqInfo(ctx).BucketName = bucketName
   337  	logger.GetReqInfo(ctx).ObjectName = objectName
   338  
   339  	_, _, s3Err = checkRequestAuthTypeCredential(ctx, r, action)
   340  	return s3Err
   341  }
   342  
   343  // checkRequestAuthTypeWithVID is similar to checkRequestAuthType
   344  // passes versionID additionally.
   345  func checkRequestAuthTypeWithVID(ctx context.Context, r *http.Request, action policy.Action, bucketName, objectName, versionID string) (s3Err APIErrorCode) {
   346  	logger.GetReqInfo(ctx).BucketName = bucketName
   347  	logger.GetReqInfo(ctx).ObjectName = objectName
   348  	logger.GetReqInfo(ctx).VersionID = versionID
   349  
   350  	_, _, s3Err = checkRequestAuthTypeCredential(ctx, r, action)
   351  	return s3Err
   352  }
   353  
   354  func authenticateRequest(ctx context.Context, r *http.Request, action policy.Action) (s3Err APIErrorCode) {
   355  	if logger.GetReqInfo(ctx) == nil {
   356  		logger.LogIf(ctx, errors.New("unexpected context.Context does not have a logger.ReqInfo"), logger.ErrorKind)
   357  		return ErrAccessDenied
   358  	}
   359  
   360  	var cred auth.Credentials
   361  	var owner bool
   362  	switch getRequestAuthType(r) {
   363  	case authTypeUnknown, authTypeStreamingSigned:
   364  		return ErrSignatureVersionNotSupported
   365  	case authTypePresignedV2, authTypeSignedV2:
   366  		if s3Err = isReqAuthenticatedV2(r); s3Err != ErrNone {
   367  			return s3Err
   368  		}
   369  		cred, owner, s3Err = getReqAccessKeyV2(r)
   370  	case authTypeSigned, authTypePresigned:
   371  		region := globalSite.Region
   372  		switch action {
   373  		case policy.GetBucketLocationAction, policy.ListAllMyBucketsAction:
   374  			region = ""
   375  		}
   376  		if s3Err = isReqAuthenticated(ctx, r, region, serviceS3); s3Err != ErrNone {
   377  			return s3Err
   378  		}
   379  		cred, owner, s3Err = getReqAccessKeyV4(r, region, serviceS3)
   380  	}
   381  	if s3Err != ErrNone {
   382  		return s3Err
   383  	}
   384  
   385  	logger.GetReqInfo(ctx).Cred = cred
   386  	logger.GetReqInfo(ctx).Owner = owner
   387  	logger.GetReqInfo(ctx).Region = globalSite.Region
   388  
   389  	// region is valid only for CreateBucketAction.
   390  	var region string
   391  	if action == policy.CreateBucketAction {
   392  		// To extract region from XML in request body, get copy of request body.
   393  		payload, err := io.ReadAll(io.LimitReader(r.Body, maxLocationConstraintSize))
   394  		if err != nil {
   395  			logger.LogIf(ctx, err, logger.ErrorKind)
   396  			return ErrMalformedXML
   397  		}
   398  
   399  		// Populate payload to extract location constraint.
   400  		r.Body = io.NopCloser(bytes.NewReader(payload))
   401  		region, s3Err = parseLocationConstraint(r)
   402  		if s3Err != ErrNone {
   403  			return s3Err
   404  		}
   405  
   406  		// Populate payload again to handle it in HTTP handler.
   407  		r.Body = io.NopCloser(bytes.NewReader(payload))
   408  	}
   409  
   410  	logger.GetReqInfo(ctx).Region = region
   411  
   412  	return s3Err
   413  }
   414  
   415  func authorizeRequest(ctx context.Context, r *http.Request, action policy.Action) (s3Err APIErrorCode) {
   416  	reqInfo := logger.GetReqInfo(ctx)
   417  	if reqInfo == nil {
   418  		return ErrAccessDenied
   419  	}
   420  
   421  	cred := reqInfo.Cred
   422  	owner := reqInfo.Owner
   423  	region := reqInfo.Region
   424  	bucket := reqInfo.BucketName
   425  	object := reqInfo.ObjectName
   426  	versionID := reqInfo.VersionID
   427  
   428  	if action != policy.ListAllMyBucketsAction && cred.AccessKey == "" {
   429  		// Anonymous checks are not meant for ListAllBuckets action
   430  		if globalPolicySys.IsAllowed(policy.BucketPolicyArgs{
   431  			AccountName:     cred.AccessKey,
   432  			Groups:          cred.Groups,
   433  			Action:          action,
   434  			BucketName:      bucket,
   435  			ConditionValues: getConditionValues(r, region, auth.AnonymousCredentials),
   436  			IsOwner:         false,
   437  			ObjectName:      object,
   438  		}) {
   439  			// Request is allowed return the appropriate access key.
   440  			return ErrNone
   441  		}
   442  
   443  		if action == policy.ListBucketVersionsAction {
   444  			// In AWS S3 s3:ListBucket permission is same as s3:ListBucketVersions permission
   445  			// verify as a fallback.
   446  			if globalPolicySys.IsAllowed(policy.BucketPolicyArgs{
   447  				AccountName:     cred.AccessKey,
   448  				Groups:          cred.Groups,
   449  				Action:          policy.ListBucketAction,
   450  				BucketName:      bucket,
   451  				ConditionValues: getConditionValues(r, region, auth.AnonymousCredentials),
   452  				IsOwner:         false,
   453  				ObjectName:      object,
   454  			}) {
   455  				// Request is allowed return the appropriate access key.
   456  				return ErrNone
   457  			}
   458  		}
   459  
   460  		return ErrAccessDenied
   461  	}
   462  	if action == policy.DeleteObjectAction && versionID != "" {
   463  		if !globalIAMSys.IsAllowed(policy.Args{
   464  			AccountName:     cred.AccessKey,
   465  			Groups:          cred.Groups,
   466  			Action:          policy.Action(policy.DeleteObjectVersionAction),
   467  			BucketName:      bucket,
   468  			ConditionValues: getConditionValues(r, "", cred),
   469  			ObjectName:      object,
   470  			IsOwner:         owner,
   471  			Claims:          cred.Claims,
   472  			DenyOnly:        true,
   473  		}) { // Request is not allowed if Deny action on DeleteObjectVersionAction
   474  			return ErrAccessDenied
   475  		}
   476  	}
   477  	if globalIAMSys.IsAllowed(policy.Args{
   478  		AccountName:     cred.AccessKey,
   479  		Groups:          cred.Groups,
   480  		Action:          action,
   481  		BucketName:      bucket,
   482  		ConditionValues: getConditionValues(r, "", cred),
   483  		ObjectName:      object,
   484  		IsOwner:         owner,
   485  		Claims:          cred.Claims,
   486  	}) {
   487  		// Request is allowed return the appropriate access key.
   488  		return ErrNone
   489  	}
   490  
   491  	if action == policy.ListBucketVersionsAction {
   492  		// In AWS S3 s3:ListBucket permission is same as s3:ListBucketVersions permission
   493  		// verify as a fallback.
   494  		if globalIAMSys.IsAllowed(policy.Args{
   495  			AccountName:     cred.AccessKey,
   496  			Groups:          cred.Groups,
   497  			Action:          policy.ListBucketAction,
   498  			BucketName:      bucket,
   499  			ConditionValues: getConditionValues(r, "", cred),
   500  			ObjectName:      object,
   501  			IsOwner:         owner,
   502  			Claims:          cred.Claims,
   503  		}) {
   504  			// Request is allowed return the appropriate access key.
   505  			return ErrNone
   506  		}
   507  	}
   508  
   509  	return ErrAccessDenied
   510  }
   511  
   512  // Check request auth type verifies the incoming http request
   513  //   - validates the request signature
   514  //   - validates the policy action if anonymous tests bucket policies if any,
   515  //     for authenticated requests validates IAM policies.
   516  //
   517  // returns APIErrorCode if any to be replied to the client.
   518  // Additionally returns the accessKey used in the request, and if this request is by an admin.
   519  func checkRequestAuthTypeCredential(ctx context.Context, r *http.Request, action policy.Action) (cred auth.Credentials, owner bool, s3Err APIErrorCode) {
   520  	s3Err = authenticateRequest(ctx, r, action)
   521  	reqInfo := logger.GetReqInfo(ctx)
   522  	if reqInfo == nil {
   523  		return cred, owner, ErrAccessDenied
   524  	}
   525  
   526  	cred = reqInfo.Cred
   527  	owner = reqInfo.Owner
   528  	if s3Err != ErrNone {
   529  		return cred, owner, s3Err
   530  	}
   531  
   532  	return cred, owner, authorizeRequest(ctx, r, action)
   533  }
   534  
   535  // Verify if request has valid AWS Signature Version '2'.
   536  func isReqAuthenticatedV2(r *http.Request) (s3Error APIErrorCode) {
   537  	if isRequestSignatureV2(r) {
   538  		return doesSignV2Match(r)
   539  	}
   540  	return doesPresignV2SignatureMatch(r)
   541  }
   542  
   543  func reqSignatureV4Verify(r *http.Request, region string, stype serviceType) (s3Error APIErrorCode) {
   544  	sha256sum := getContentSha256Cksum(r, stype)
   545  	switch {
   546  	case isRequestSignatureV4(r):
   547  		return doesSignatureMatch(sha256sum, r, region, stype)
   548  	case isRequestPresignedSignatureV4(r):
   549  		return doesPresignedSignatureMatch(sha256sum, r, region, stype)
   550  	default:
   551  		return ErrAccessDenied
   552  	}
   553  }
   554  
   555  // Verify if request has valid AWS Signature Version '4'.
   556  func isReqAuthenticated(ctx context.Context, r *http.Request, region string, stype serviceType) (s3Error APIErrorCode) {
   557  	if errCode := reqSignatureV4Verify(r, region, stype); errCode != ErrNone {
   558  		return errCode
   559  	}
   560  
   561  	clientETag, err := etag.FromContentMD5(r.Header)
   562  	if err != nil {
   563  		return ErrInvalidDigest
   564  	}
   565  
   566  	// Extract either 'X-Amz-Content-Sha256' header or 'X-Amz-Content-Sha256' query parameter (if V4 presigned)
   567  	// Do not verify 'X-Amz-Content-Sha256' if skipSHA256.
   568  	var contentSHA256 []byte
   569  	if skipSHA256 := skipContentSha256Cksum(r); !skipSHA256 && isRequestPresignedSignatureV4(r) {
   570  		if sha256Sum, ok := r.Form[xhttp.AmzContentSha256]; ok && len(sha256Sum) > 0 {
   571  			contentSHA256, err = hex.DecodeString(sha256Sum[0])
   572  			if err != nil {
   573  				return ErrContentSHA256Mismatch
   574  			}
   575  		}
   576  	} else if _, ok := r.Header[xhttp.AmzContentSha256]; !skipSHA256 && ok {
   577  		contentSHA256, err = hex.DecodeString(r.Header.Get(xhttp.AmzContentSha256))
   578  		if err != nil || len(contentSHA256) == 0 {
   579  			return ErrContentSHA256Mismatch
   580  		}
   581  	}
   582  
   583  	// Verify 'Content-Md5' and/or 'X-Amz-Content-Sha256' if present.
   584  	// The verification happens implicit during reading.
   585  	reader, err := hash.NewReader(ctx, r.Body, -1, clientETag.String(), hex.EncodeToString(contentSHA256), -1)
   586  	if err != nil {
   587  		return toAPIErrorCode(ctx, err)
   588  	}
   589  	r.Body = reader
   590  	return ErrNone
   591  }
   592  
   593  // List of all support S3 auth types.
   594  var supportedS3AuthTypes = map[authType]struct{}{
   595  	authTypeAnonymous:                {},
   596  	authTypePresigned:                {},
   597  	authTypePresignedV2:              {},
   598  	authTypeSigned:                   {},
   599  	authTypeSignedV2:                 {},
   600  	authTypePostPolicy:               {},
   601  	authTypeStreamingSigned:          {},
   602  	authTypeStreamingSignedTrailer:   {},
   603  	authTypeStreamingUnsignedTrailer: {},
   604  }
   605  
   606  // Validate if the authType is valid and supported.
   607  func isSupportedS3AuthType(aType authType) bool {
   608  	_, ok := supportedS3AuthTypes[aType]
   609  	return ok
   610  }
   611  
   612  // setAuthMiddleware to validate authorization header for the incoming request.
   613  func setAuthMiddleware(h http.Handler) http.Handler {
   614  	// handler for validating incoming authorization headers.
   615  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   616  		tc, ok := r.Context().Value(mcontext.ContextTraceKey).(*mcontext.TraceCtxt)
   617  
   618  		aType := getRequestAuthType(r)
   619  		switch aType {
   620  		case authTypeSigned, authTypeSignedV2, authTypeStreamingSigned, authTypeStreamingSignedTrailer:
   621  			// Verify if date headers are set, if not reject the request
   622  			amzDate, errCode := parseAmzDateHeader(r)
   623  			if errCode != ErrNone {
   624  				if ok {
   625  					tc.FuncName = "handler.Auth"
   626  					tc.ResponseRecorder.LogErrBody = true
   627  				}
   628  
   629  				// All our internal APIs are sensitive towards Date
   630  				// header, for all requests where Date header is not
   631  				// present we will reject such clients.
   632  				defer logger.AuditLog(r.Context(), w, r, mustGetClaimsFromToken(r))
   633  				writeErrorResponse(r.Context(), w, errorCodes.ToAPIErr(errCode), r.URL)
   634  				atomic.AddUint64(&globalHTTPStats.rejectedRequestsTime, 1)
   635  				return
   636  			}
   637  			// Verify if the request date header is shifted by less than globalMaxSkewTime parameter in the past
   638  			// or in the future, reject request otherwise.
   639  			curTime := UTCNow()
   640  			if curTime.Sub(amzDate) > globalMaxSkewTime || amzDate.Sub(curTime) > globalMaxSkewTime {
   641  				if ok {
   642  					tc.FuncName = "handler.Auth"
   643  					tc.ResponseRecorder.LogErrBody = true
   644  				}
   645  
   646  				defer logger.AuditLog(r.Context(), w, r, mustGetClaimsFromToken(r))
   647  				writeErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrRequestTimeTooSkewed), r.URL)
   648  				atomic.AddUint64(&globalHTTPStats.rejectedRequestsTime, 1)
   649  				return
   650  			}
   651  			h.ServeHTTP(w, r)
   652  			return
   653  		case authTypeJWT, authTypeSTS:
   654  			h.ServeHTTP(w, r)
   655  			return
   656  		default:
   657  			if isSupportedS3AuthType(aType) {
   658  				h.ServeHTTP(w, r)
   659  				return
   660  			}
   661  		}
   662  
   663  		if ok {
   664  			tc.FuncName = "handler.Auth"
   665  			tc.ResponseRecorder.LogErrBody = true
   666  		}
   667  
   668  		defer logger.AuditLog(r.Context(), w, r, mustGetClaimsFromToken(r))
   669  		writeErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrSignatureVersionNotSupported), r.URL)
   670  		atomic.AddUint64(&globalHTTPStats.rejectedRequestsAuth, 1)
   671  	})
   672  }
   673  
   674  func validateSignature(atype authType, r *http.Request) (auth.Credentials, bool, APIErrorCode) {
   675  	var cred auth.Credentials
   676  	var owner bool
   677  	var s3Err APIErrorCode
   678  	switch atype {
   679  	case authTypeUnknown, authTypeStreamingSigned:
   680  		return cred, owner, ErrSignatureVersionNotSupported
   681  	case authTypeSignedV2, authTypePresignedV2:
   682  		if s3Err = isReqAuthenticatedV2(r); s3Err != ErrNone {
   683  			return cred, owner, s3Err
   684  		}
   685  		cred, owner, s3Err = getReqAccessKeyV2(r)
   686  	case authTypePresigned, authTypeSigned:
   687  		region := globalSite.Region
   688  		if s3Err = isReqAuthenticated(GlobalContext, r, region, serviceS3); s3Err != ErrNone {
   689  			return cred, owner, s3Err
   690  		}
   691  		cred, owner, s3Err = getReqAccessKeyV4(r, region, serviceS3)
   692  	}
   693  	if s3Err != ErrNone {
   694  		return cred, owner, s3Err
   695  	}
   696  
   697  	return cred, owner, ErrNone
   698  }
   699  
   700  func isPutRetentionAllowed(bucketName, objectName string, retDays int, retDate time.Time, retMode objectlock.RetMode, byPassSet bool, r *http.Request, cred auth.Credentials, owner bool) (s3Err APIErrorCode) {
   701  	var retSet bool
   702  	if cred.AccessKey == "" {
   703  		return ErrAccessDenied
   704  	}
   705  
   706  	conditions := getConditionValues(r, "", cred)
   707  	conditions["object-lock-mode"] = []string{string(retMode)}
   708  	conditions["object-lock-retain-until-date"] = []string{retDate.UTC().Format(time.RFC3339)}
   709  	if retDays > 0 {
   710  		conditions["object-lock-remaining-retention-days"] = []string{strconv.Itoa(retDays)}
   711  	}
   712  	if retMode == objectlock.RetGovernance && byPassSet {
   713  		byPassSet = globalIAMSys.IsAllowed(policy.Args{
   714  			AccountName:     cred.AccessKey,
   715  			Groups:          cred.Groups,
   716  			Action:          policy.BypassGovernanceRetentionAction,
   717  			BucketName:      bucketName,
   718  			ObjectName:      objectName,
   719  			ConditionValues: conditions,
   720  			IsOwner:         owner,
   721  			Claims:          cred.Claims,
   722  		})
   723  	}
   724  	if globalIAMSys.IsAllowed(policy.Args{
   725  		AccountName:     cred.AccessKey,
   726  		Groups:          cred.Groups,
   727  		Action:          policy.PutObjectRetentionAction,
   728  		BucketName:      bucketName,
   729  		ConditionValues: conditions,
   730  		ObjectName:      objectName,
   731  		IsOwner:         owner,
   732  		Claims:          cred.Claims,
   733  	}) {
   734  		retSet = true
   735  	}
   736  	if byPassSet || retSet {
   737  		return ErrNone
   738  	}
   739  	return ErrAccessDenied
   740  }
   741  
   742  // isPutActionAllowed - check if PUT operation is allowed on the resource, this
   743  // call verifies bucket policies and IAM policies, supports multi user
   744  // checks etc.
   745  func isPutActionAllowed(ctx context.Context, atype authType, bucketName, objectName string, r *http.Request, action policy.Action) (s3Err APIErrorCode) {
   746  	var cred auth.Credentials
   747  	var owner bool
   748  	region := globalSite.Region
   749  	switch atype {
   750  	case authTypeUnknown:
   751  		return ErrSignatureVersionNotSupported
   752  	case authTypeSignedV2, authTypePresignedV2:
   753  		cred, owner, s3Err = getReqAccessKeyV2(r)
   754  	case authTypeStreamingSigned, authTypePresigned, authTypeSigned, authTypeStreamingSignedTrailer, authTypeStreamingUnsignedTrailer:
   755  		cred, owner, s3Err = getReqAccessKeyV4(r, region, serviceS3)
   756  	}
   757  	if s3Err != ErrNone {
   758  		return s3Err
   759  	}
   760  
   761  	logger.GetReqInfo(ctx).Cred = cred
   762  	logger.GetReqInfo(ctx).Owner = owner
   763  	logger.GetReqInfo(ctx).Region = region
   764  
   765  	// Do not check for PutObjectRetentionAction permission,
   766  	// if mode and retain until date are not set.
   767  	// Can happen when bucket has default lock config set
   768  	if action == policy.PutObjectRetentionAction &&
   769  		r.Header.Get(xhttp.AmzObjectLockMode) == "" &&
   770  		r.Header.Get(xhttp.AmzObjectLockRetainUntilDate) == "" {
   771  		return ErrNone
   772  	}
   773  
   774  	if cred.AccessKey == "" {
   775  		if globalPolicySys.IsAllowed(policy.BucketPolicyArgs{
   776  			AccountName:     cred.AccessKey,
   777  			Groups:          cred.Groups,
   778  			Action:          action,
   779  			BucketName:      bucketName,
   780  			ConditionValues: getConditionValues(r, "", auth.AnonymousCredentials),
   781  			IsOwner:         false,
   782  			ObjectName:      objectName,
   783  		}) {
   784  			return ErrNone
   785  		}
   786  		return ErrAccessDenied
   787  	}
   788  
   789  	if globalIAMSys.IsAllowed(policy.Args{
   790  		AccountName:     cred.AccessKey,
   791  		Groups:          cred.Groups,
   792  		Action:          action,
   793  		BucketName:      bucketName,
   794  		ConditionValues: getConditionValues(r, "", cred),
   795  		ObjectName:      objectName,
   796  		IsOwner:         owner,
   797  		Claims:          cred.Claims,
   798  	}) {
   799  		return ErrNone
   800  	}
   801  	return ErrAccessDenied
   802  }