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 }