storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/auth-handler.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2015-2018 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "bytes" 21 "context" 22 "crypto/subtle" 23 "encoding/base64" 24 "encoding/hex" 25 "errors" 26 "io" 27 "io/ioutil" 28 "net/http" 29 "strconv" 30 "strings" 31 "sync/atomic" 32 "time" 33 34 xhttp "storj.io/minio/cmd/http" 35 xjwt "storj.io/minio/cmd/jwt" 36 "storj.io/minio/cmd/logger" 37 "storj.io/minio/pkg/auth" 38 objectlock "storj.io/minio/pkg/bucket/object/lock" 39 "storj.io/minio/pkg/bucket/policy" 40 "storj.io/minio/pkg/etag" 41 "storj.io/minio/pkg/hash" 42 iampolicy "storj.io/minio/pkg/iam/policy" 43 ) 44 45 // Verify if request has JWT. 46 func isRequestJWT(r *http.Request) bool { 47 return strings.HasPrefix(r.Header.Get(xhttp.Authorization), jwtAlgorithm) 48 } 49 50 // Verify if request has AWS Signature Version '4'. 51 func isRequestSignatureV4(r *http.Request) bool { 52 return strings.HasPrefix(r.Header.Get(xhttp.Authorization), signV4Algorithm) 53 } 54 55 // Verify if request has AWS Signature Version '2'. 56 func isRequestSignatureV2(r *http.Request) bool { 57 return (!strings.HasPrefix(r.Header.Get(xhttp.Authorization), signV4Algorithm) && 58 strings.HasPrefix(r.Header.Get(xhttp.Authorization), signV2Algorithm)) 59 } 60 61 // Verify if request has AWS PreSign Version '4'. 62 func isRequestPresignedSignatureV4(r *http.Request) bool { 63 _, ok := r.URL.Query()[xhttp.AmzCredential] 64 return ok 65 } 66 67 // Verify request has AWS PreSign Version '2'. 68 func isRequestPresignedSignatureV2(r *http.Request) bool { 69 _, ok := r.URL.Query()[xhttp.AmzAccessKeyID] 70 return ok 71 } 72 73 // Verify if request has AWS Post policy Signature Version '4'. 74 func isRequestPostPolicySignatureV4(r *http.Request) bool { 75 return strings.Contains(r.Header.Get(xhttp.ContentType), "multipart/form-data") && 76 r.Method == http.MethodPost 77 } 78 79 // Verify if the request has AWS Streaming Signature Version '4'. This is only valid for 'PUT' operation. 80 func isRequestSignStreamingV4(r *http.Request) bool { 81 return r.Header.Get(xhttp.AmzContentSha256) == streamingContentSHA256 && 82 r.Method == http.MethodPut 83 } 84 85 // Authorization type. 86 type authType int 87 88 // List of all supported auth types. 89 const ( 90 authTypeUnknown authType = iota 91 authTypeAnonymous 92 authTypePresigned 93 authTypePresignedV2 94 authTypePostPolicy 95 authTypeStreamingSigned 96 authTypeSigned 97 authTypeSignedV2 98 authTypeJWT 99 authTypeSTS 100 ) 101 102 // Get request authentication type. 103 func getRequestAuthType(r *http.Request) authType { 104 if isRequestSignatureV2(r) { 105 return authTypeSignedV2 106 } else if isRequestPresignedSignatureV2(r) { 107 return authTypePresignedV2 108 } else if isRequestSignStreamingV4(r) { 109 return authTypeStreamingSigned 110 } else if isRequestSignatureV4(r) { 111 return authTypeSigned 112 } else if isRequestPresignedSignatureV4(r) { 113 return authTypePresigned 114 } else if isRequestJWT(r) { 115 return authTypeJWT 116 } else if isRequestPostPolicySignatureV4(r) { 117 return authTypePostPolicy 118 } else if _, ok := r.URL.Query()[xhttp.Action]; ok { 119 return authTypeSTS 120 } else if _, ok := r.Header[xhttp.Authorization]; !ok { 121 return authTypeAnonymous 122 } 123 return authTypeUnknown 124 } 125 126 func validateAdminSignature(ctx context.Context, r *http.Request, region string) (auth.Credentials, map[string]interface{}, bool, APIErrorCode) { 127 var cred auth.Credentials 128 var owner bool 129 s3Err := ErrAccessDenied 130 if _, ok := r.Header[xhttp.AmzContentSha256]; ok && 131 getRequestAuthType(r) == authTypeSigned && !skipContentSha256Cksum(r) { 132 // We only support admin credentials to access admin APIs. 133 cred, owner, s3Err = getReqAccessKeyV4(r, region, serviceS3) 134 if s3Err != ErrNone { 135 return cred, nil, owner, s3Err 136 } 137 138 // we only support V4 (no presign) with auth body 139 s3Err = isReqAuthenticated(ctx, r, region, serviceS3) 140 } 141 if s3Err != ErrNone { 142 reqInfo := (&logger.ReqInfo{}).AppendTags("requestHeaders", dumpRequest(r)) 143 ctx := logger.SetReqInfo(ctx, reqInfo) 144 logger.LogIf(ctx, errors.New(GetAPIError(s3Err).Description), logger.Application) 145 return cred, nil, owner, s3Err 146 } 147 148 claims, s3Err := checkClaimsFromToken(r, cred) 149 if s3Err != ErrNone { 150 return cred, nil, owner, s3Err 151 } 152 153 return cred, claims, owner, ErrNone 154 } 155 156 // checkAdminRequestAuth checks for authentication and authorization for the incoming 157 // request. It only accepts V2 and V4 requests. Presigned, JWT and anonymous requests 158 // are automatically rejected. 159 func checkAdminRequestAuth(ctx context.Context, r *http.Request, action iampolicy.AdminAction, region string) (auth.Credentials, APIErrorCode) { 160 cred, claims, owner, s3Err := validateAdminSignature(ctx, r, region) 161 if s3Err != ErrNone { 162 return cred, s3Err 163 } 164 if GlobalIAMSys.IsAllowed(iampolicy.Args{ 165 AccountName: cred.AccessKey, 166 Groups: cred.Groups, 167 Action: iampolicy.Action(action), 168 ConditionValues: getConditionValues(r, "", cred.AccessKey, claims), 169 IsOwner: owner, 170 Claims: claims, 171 }) { 172 // Request is allowed return the appropriate access key. 173 return cred, ErrNone 174 } 175 176 return cred, ErrAccessDenied 177 } 178 179 // Fetch the security token set by the client. 180 func getSessionToken(r *http.Request) (token string) { 181 token = r.Header.Get(xhttp.AmzSecurityToken) 182 if token != "" { 183 return token 184 } 185 return r.URL.Query().Get(xhttp.AmzSecurityToken) 186 } 187 188 // Fetch claims in the security token returned by the client, doesn't return 189 // errors - upon errors the returned claims map will be empty. 190 func mustGetClaimsFromToken(r *http.Request) map[string]interface{} { 191 claims, _ := getClaimsFromToken(getSessionToken(r)) 192 return claims 193 } 194 195 // Fetch claims in the security token returned by the client. 196 func getClaimsFromToken(token string) (map[string]interface{}, error) { 197 if token == "" { 198 claims := xjwt.NewMapClaims() 199 return claims.Map(), nil 200 } 201 202 // JWT token for x-amz-security-token is signed with admin 203 // secret key, temporary credentials become invalid if 204 // server admin credentials change. This is done to ensure 205 // that clients cannot decode the token using the temp 206 // secret keys and generate an entirely new claim by essentially 207 // hijacking the policies. We need to make sure that this is 208 // based an admin credential such that token cannot be decoded 209 // on the client side and is treated like an opaque value. 210 claims, err := auth.ExtractClaims(token, globalActiveCred.SecretKey) 211 if err != nil { 212 return nil, errAuthentication 213 } 214 215 if GlobalPolicyOPA == nil { 216 // If OPA is not set and if ldap claim key is set, allow the claim. 217 if _, ok := claims.MapClaims[ldapUser]; ok { 218 return claims.Map(), nil 219 } 220 221 // If OPA is not set, session token should 222 // have a policy and its mandatory, reject 223 // requests without policy claim. 224 _, pokOpenID := claims.MapClaims[iamPolicyClaimNameOpenID()] 225 _, pokSA := claims.MapClaims[iamPolicyClaimNameSA()] 226 if !pokOpenID && !pokSA { 227 return nil, errAuthentication 228 } 229 230 sp, spok := claims.Lookup(iampolicy.SessionPolicyName) 231 if !spok { 232 return claims.Map(), nil 233 } 234 // Looks like subpolicy is set and is a string, if set then its 235 // base64 encoded, decode it. Decoding fails reject such requests. 236 spBytes, err := base64.StdEncoding.DecodeString(sp) 237 if err != nil { 238 // Base64 decoding fails, we should log to indicate 239 // something is malforming the request sent by client. 240 logger.LogIf(GlobalContext, err, logger.Application) 241 return nil, errAuthentication 242 } 243 claims.MapClaims[iampolicy.SessionPolicyName] = string(spBytes) 244 } 245 246 return claims.Map(), nil 247 } 248 249 // Fetch claims in the security token returned by the client and validate the token. 250 func checkClaimsFromToken(r *http.Request, cred auth.Credentials) (map[string]interface{}, APIErrorCode) { 251 token := getSessionToken(r) 252 if token != "" && cred.AccessKey == "" { 253 return nil, ErrNoAccessKey 254 } 255 if cred.IsServiceAccount() && token == "" { 256 token = cred.SessionToken 257 } 258 if subtle.ConstantTimeCompare([]byte(token), []byte(cred.SessionToken)) != 1 { 259 return nil, ErrInvalidToken 260 } 261 claims, err := getClaimsFromToken(token) 262 if err != nil { 263 return nil, toAPIErrorCode(r.Context(), err) 264 } 265 return claims, ErrNone 266 } 267 268 // Check request auth type verifies the incoming http request 269 // - validates the request signature 270 // - validates the policy action if anonymous tests bucket policies if any, 271 // for authenticated requests validates IAM policies. 272 // returns APIErrorCode if any to be replied to the client. 273 func checkRequestAuthType(ctx context.Context, r *http.Request, action policy.Action, bucketName, objectName string) (s3Err APIErrorCode) { 274 _, _, s3Err = CheckRequestAuthTypeCredential(ctx, r, action, bucketName, objectName) 275 return s3Err 276 } 277 278 // Check request auth type verifies the incoming http request 279 // - validates the request signature 280 // - validates the policy action if anonymous tests bucket policies if any, 281 // for authenticated requests validates IAM policies. 282 // returns APIErrorCode if any to be replied to the client. 283 // Additionally returns the accessKey used in the request, and if this request is by an admin. 284 func CheckRequestAuthTypeCredential(ctx context.Context, r *http.Request, action policy.Action, bucketName, objectName string) (cred auth.Credentials, owner bool, s3Err APIErrorCode) { 285 switch getRequestAuthType(r) { 286 case authTypeUnknown, authTypeStreamingSigned: 287 return cred, owner, ErrSignatureVersionNotSupported 288 case authTypePresignedV2, authTypeSignedV2: 289 if s3Err = isReqAuthenticatedV2(r); s3Err != ErrNone { 290 return cred, owner, s3Err 291 } 292 cred, owner, s3Err = getReqAccessKeyV2(r) 293 case authTypeSigned, authTypePresigned: 294 region := globalServerRegion 295 switch action { 296 case policy.GetBucketLocationAction, policy.ListAllMyBucketsAction: 297 region = "" 298 } 299 if s3Err = isReqAuthenticated(ctx, r, region, serviceS3); s3Err != ErrNone { 300 return cred, owner, s3Err 301 } 302 cred, owner, s3Err = getReqAccessKeyV4(r, region, serviceS3) 303 } 304 if s3Err != ErrNone { 305 return cred, owner, s3Err 306 } 307 308 var claims map[string]interface{} 309 claims, s3Err = checkClaimsFromToken(r, cred) 310 if s3Err != ErrNone { 311 return cred, owner, s3Err 312 } 313 314 // LocationConstraint is valid only for CreateBucketAction. 315 var locationConstraint string 316 if action == policy.CreateBucketAction { 317 // To extract region from XML in request body, get copy of request body. 318 payload, err := ioutil.ReadAll(io.LimitReader(r.Body, maxLocationConstraintSize)) 319 if err != nil { 320 logger.LogIf(ctx, err, logger.Application) 321 return cred, owner, ErrMalformedXML 322 } 323 324 // Populate payload to extract location constraint. 325 r.Body = ioutil.NopCloser(bytes.NewReader(payload)) 326 327 var s3Error APIErrorCode 328 locationConstraint, s3Error = parseLocationConstraint(r) 329 if s3Error != ErrNone { 330 return cred, owner, s3Error 331 } 332 333 // Populate payload again to handle it in HTTP handler. 334 r.Body = ioutil.NopCloser(bytes.NewReader(payload)) 335 } 336 if cred.AccessKey != "" { 337 logger.GetReqInfo(ctx).AccessKey = cred.AccessKey 338 } 339 if cred.AccessGrant != "" { 340 logger.GetReqInfo(ctx).AccessGrant = cred.AccessGrant 341 } 342 343 if action != policy.ListAllMyBucketsAction && cred.AccessKey == "" { 344 // Anonymous checks are not meant for ListBuckets action 345 if globalPolicySys.IsAllowed(policy.Args{ 346 AccountName: cred.AccessKey, 347 Action: action, 348 BucketName: bucketName, 349 ConditionValues: getConditionValues(r, locationConstraint, "", nil), 350 IsOwner: false, 351 ObjectName: objectName, 352 }) { 353 // Request is allowed return the appropriate access key. 354 return cred, owner, ErrNone 355 } 356 357 if action == policy.ListBucketVersionsAction { 358 // In AWS S3 s3:ListBucket permission is same as s3:ListBucketVersions permission 359 // verify as a fallback. 360 if globalPolicySys.IsAllowed(policy.Args{ 361 AccountName: cred.AccessKey, 362 Action: policy.ListBucketAction, 363 BucketName: bucketName, 364 ConditionValues: getConditionValues(r, locationConstraint, "", nil), 365 IsOwner: false, 366 ObjectName: objectName, 367 }) { 368 // Request is allowed return the appropriate access key. 369 return cred, owner, ErrNone 370 } 371 } 372 373 return cred, owner, ErrAccessDenied 374 } 375 376 if GlobalIAMSys.IsAllowed(iampolicy.Args{ 377 AccountName: cred.AccessKey, 378 Groups: cred.Groups, 379 Action: iampolicy.Action(action), 380 BucketName: bucketName, 381 ConditionValues: getConditionValues(r, "", cred.AccessKey, claims), 382 ObjectName: objectName, 383 IsOwner: owner, 384 Claims: claims, 385 }) { 386 // Request is allowed return the appropriate access key. 387 return cred, owner, ErrNone 388 } 389 390 if action == policy.ListBucketVersionsAction { 391 // In AWS S3 s3:ListBucket permission is same as s3:ListBucketVersions permission 392 // verify as a fallback. 393 if GlobalIAMSys.IsAllowed(iampolicy.Args{ 394 AccountName: cred.AccessKey, 395 Groups: cred.Groups, 396 Action: iampolicy.ListBucketAction, 397 BucketName: bucketName, 398 ConditionValues: getConditionValues(r, "", cred.AccessKey, claims), 399 ObjectName: objectName, 400 IsOwner: owner, 401 Claims: claims, 402 }) { 403 // Request is allowed return the appropriate access key. 404 return cred, owner, ErrNone 405 } 406 } 407 408 return cred, owner, ErrAccessDenied 409 } 410 411 // Verify if request has valid AWS Signature Version '2'. 412 func isReqAuthenticatedV2(r *http.Request) (s3Error APIErrorCode) { 413 if isRequestSignatureV2(r) { 414 return doesSignV2Match(r) 415 } 416 return doesPresignV2SignatureMatch(r) 417 } 418 419 func reqSignatureV4Verify(r *http.Request, region string, stype serviceType) (s3Error APIErrorCode) { 420 sha256sum := getContentSha256Cksum(r, stype) 421 switch { 422 case isRequestSignatureV4(r): 423 return doesSignatureMatch(sha256sum, r, region, stype) 424 case isRequestPresignedSignatureV4(r): 425 return doesPresignedSignatureMatch(sha256sum, r, region, stype) 426 default: 427 return ErrAccessDenied 428 } 429 } 430 431 // Verify if request has valid AWS Signature Version '4'. 432 func isReqAuthenticated(ctx context.Context, r *http.Request, region string, stype serviceType) (s3Error APIErrorCode) { 433 if errCode := reqSignatureV4Verify(r, region, stype); errCode != ErrNone { 434 return errCode 435 } 436 437 clientETag, err := etag.FromContentMD5(r.Header) 438 if err != nil { 439 return ErrInvalidDigest 440 } 441 442 // Extract either 'X-Amz-Content-Sha256' header or 'X-Amz-Content-Sha256' query parameter (if V4 presigned) 443 // Do not verify 'X-Amz-Content-Sha256' if skipSHA256. 444 var contentSHA256 []byte 445 if skipSHA256 := skipContentSha256Cksum(r); !skipSHA256 && isRequestPresignedSignatureV4(r) { 446 if sha256Sum, ok := r.URL.Query()[xhttp.AmzContentSha256]; ok && len(sha256Sum) > 0 { 447 contentSHA256, err = hex.DecodeString(sha256Sum[0]) 448 if err != nil { 449 return ErrContentSHA256Mismatch 450 } 451 } 452 } else if _, ok := r.Header[xhttp.AmzContentSha256]; !skipSHA256 && ok { 453 contentSHA256, err = hex.DecodeString(r.Header.Get(xhttp.AmzContentSha256)) 454 if err != nil || len(contentSHA256) == 0 { 455 return ErrContentSHA256Mismatch 456 } 457 } 458 459 // Verify 'Content-Md5' and/or 'X-Amz-Content-Sha256' if present. 460 // The verification happens implicit during reading. 461 reader, err := hash.NewReader(r.Body, -1, clientETag.String(), hex.EncodeToString(contentSHA256), -1) 462 if err != nil { 463 return toAPIErrorCode(ctx, err) 464 } 465 r.Body = reader 466 return ErrNone 467 } 468 469 // List of all support S3 auth types. 470 var supportedS3AuthTypes = map[authType]struct{}{ 471 authTypeAnonymous: {}, 472 authTypePresigned: {}, 473 authTypePresignedV2: {}, 474 authTypeSigned: {}, 475 authTypeSignedV2: {}, 476 authTypePostPolicy: {}, 477 authTypeStreamingSigned: {}, 478 } 479 480 // Validate if the authType is valid and supported. 481 func isSupportedS3AuthType(aType authType) bool { 482 _, ok := supportedS3AuthTypes[aType] 483 return ok 484 } 485 486 // setAuthHandler to validate authorization header for the incoming request. 487 func setAuthHandler(h http.Handler) http.Handler { 488 // handler for validating incoming authorization headers. 489 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 490 aType := getRequestAuthType(r) 491 if isSupportedS3AuthType(aType) { 492 // Let top level caller validate for anonymous and known signed requests. 493 h.ServeHTTP(w, r) 494 return 495 } else if aType == authTypeJWT { 496 // Validate Authorization header if its valid for JWT request. 497 if _, _, authErr := webRequestAuthenticate(r); authErr != nil { 498 w.WriteHeader(http.StatusUnauthorized) 499 w.Write([]byte(authErr.Error())) 500 return 501 } 502 h.ServeHTTP(w, r) 503 return 504 } else if aType == authTypeSTS { 505 h.ServeHTTP(w, r) 506 return 507 } 508 // NOTE(artur): for requests having an empty Authorization header, the 509 // "real" S3 returns the AccessDenied error code. We do the same thing 510 // to be compatible with ceph/splunk-s3-tests. There are plenty of other 511 // incompatibilities in there, but they are not easily fixable with how 512 // MinIO is structured, so we do at least this. 513 WriteErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL, guessIsBrowserReq(r)) 514 atomic.AddUint64(&globalHTTPStats.rejectedRequestsAuth, 1) 515 }) 516 } 517 518 func validateSignature(atype authType, r *http.Request) (auth.Credentials, bool, map[string]interface{}, APIErrorCode) { 519 var cred auth.Credentials 520 var owner bool 521 var s3Err APIErrorCode 522 switch atype { 523 case authTypeUnknown, authTypeStreamingSigned: 524 return cred, owner, nil, ErrSignatureVersionNotSupported 525 case authTypeSignedV2, authTypePresignedV2: 526 if s3Err = isReqAuthenticatedV2(r); s3Err != ErrNone { 527 return cred, owner, nil, s3Err 528 } 529 cred, owner, s3Err = getReqAccessKeyV2(r) 530 case authTypePresigned, authTypeSigned: 531 region := globalServerRegion 532 if s3Err = isReqAuthenticated(GlobalContext, r, region, serviceS3); s3Err != ErrNone { 533 return cred, owner, nil, s3Err 534 } 535 cred, owner, s3Err = getReqAccessKeyV4(r, region, serviceS3) 536 } 537 if s3Err != ErrNone { 538 return cred, owner, nil, s3Err 539 } 540 541 claims, s3Err := checkClaimsFromToken(r, cred) 542 if s3Err != ErrNone { 543 return cred, owner, nil, s3Err 544 } 545 546 return cred, owner, claims, ErrNone 547 } 548 549 func isPutRetentionAllowed(bucketName, objectName string, retDays int, retDate time.Time, retMode objectlock.RetMode, byPassSet bool, r *http.Request, cred auth.Credentials, owner bool, claims map[string]interface{}) (s3Err APIErrorCode) { 550 var retSet bool 551 if cred.AccessKey == "" { 552 conditions := getConditionValues(r, "", "", nil) 553 conditions["object-lock-mode"] = []string{string(retMode)} 554 conditions["object-lock-retain-until-date"] = []string{retDate.Format(time.RFC3339)} 555 if retDays > 0 { 556 conditions["object-lock-remaining-retention-days"] = []string{strconv.Itoa(retDays)} 557 } 558 if retMode == objectlock.RetGovernance && byPassSet { 559 byPassSet = globalPolicySys.IsAllowed(policy.Args{ 560 AccountName: cred.AccessKey, 561 Groups: cred.Groups, 562 Action: policy.BypassGovernanceRetentionAction, 563 BucketName: bucketName, 564 ConditionValues: conditions, 565 IsOwner: false, 566 ObjectName: objectName, 567 }) 568 } 569 if globalPolicySys.IsAllowed(policy.Args{ 570 AccountName: cred.AccessKey, 571 Groups: cred.Groups, 572 Action: policy.PutObjectRetentionAction, 573 BucketName: bucketName, 574 ConditionValues: conditions, 575 IsOwner: false, 576 ObjectName: objectName, 577 }) { 578 retSet = true 579 } 580 if byPassSet || retSet { 581 return ErrNone 582 } 583 return ErrAccessDenied 584 } 585 586 conditions := getConditionValues(r, "", cred.AccessKey, claims) 587 conditions["object-lock-mode"] = []string{string(retMode)} 588 conditions["object-lock-retain-until-date"] = []string{retDate.Format(time.RFC3339)} 589 if retDays > 0 { 590 conditions["object-lock-remaining-retention-days"] = []string{strconv.Itoa(retDays)} 591 } 592 if retMode == objectlock.RetGovernance && byPassSet { 593 byPassSet = GlobalIAMSys.IsAllowed(iampolicy.Args{ 594 AccountName: cred.AccessKey, 595 Groups: cred.Groups, 596 Action: iampolicy.BypassGovernanceRetentionAction, 597 BucketName: bucketName, 598 ObjectName: objectName, 599 ConditionValues: conditions, 600 IsOwner: owner, 601 Claims: claims, 602 }) 603 } 604 if GlobalIAMSys.IsAllowed(iampolicy.Args{ 605 AccountName: cred.AccessKey, 606 Groups: cred.Groups, 607 Action: iampolicy.PutObjectRetentionAction, 608 BucketName: bucketName, 609 ConditionValues: conditions, 610 ObjectName: objectName, 611 IsOwner: owner, 612 Claims: claims, 613 }) { 614 retSet = true 615 } 616 if byPassSet || retSet { 617 return ErrNone 618 } 619 return ErrAccessDenied 620 } 621 622 // isPutActionAllowed - check if PUT operation is allowed on the resource, this 623 // call verifies bucket policies and IAM policies, supports multi user 624 // checks etc. 625 func isPutActionAllowed(ctx context.Context, atype authType, bucketName, objectName string, r *http.Request, action iampolicy.Action) (s3Err APIErrorCode) { 626 var cred auth.Credentials 627 var owner bool 628 switch atype { 629 case authTypeUnknown: 630 return ErrSignatureVersionNotSupported 631 case authTypeSignedV2, authTypePresignedV2: 632 cred, owner, s3Err = getReqAccessKeyV2(r) 633 case authTypeStreamingSigned, authTypePresigned, authTypeSigned: 634 region := globalServerRegion 635 cred, owner, s3Err = getReqAccessKeyV4(r, region, serviceS3) 636 } 637 if s3Err != ErrNone { 638 return s3Err 639 } 640 641 claims, s3Err := checkClaimsFromToken(r, cred) 642 if s3Err != ErrNone { 643 return s3Err 644 } 645 646 if cred.AccessKey != "" { 647 logger.GetReqInfo(ctx).AccessKey = cred.AccessKey 648 } 649 if cred.AccessGrant != "" { 650 logger.GetReqInfo(ctx).AccessGrant = cred.AccessGrant 651 } 652 653 // Do not check for PutObjectRetentionAction permission, 654 // if mode and retain until date are not set. 655 // Can happen when bucket has default lock config set 656 if action == iampolicy.PutObjectRetentionAction && 657 r.Header.Get(xhttp.AmzObjectLockMode) == "" && 658 r.Header.Get(xhttp.AmzObjectLockRetainUntilDate) == "" { 659 return ErrNone 660 } 661 662 if cred.AccessKey == "" { 663 if globalPolicySys.IsAllowed(policy.Args{ 664 AccountName: cred.AccessKey, 665 Groups: cred.Groups, 666 Action: policy.Action(action), 667 BucketName: bucketName, 668 ConditionValues: getConditionValues(r, "", "", nil), 669 IsOwner: false, 670 ObjectName: objectName, 671 }) { 672 return ErrNone 673 } 674 return ErrAccessDenied 675 } 676 677 if GlobalIAMSys.IsAllowed(iampolicy.Args{ 678 AccountName: cred.AccessKey, 679 Groups: cred.Groups, 680 Action: action, 681 BucketName: bucketName, 682 ConditionValues: getConditionValues(r, "", cred.AccessKey, claims), 683 ObjectName: objectName, 684 IsOwner: owner, 685 Claims: claims, 686 }) { 687 return ErrNone 688 } 689 return ErrAccessDenied 690 }