diff --git a/integration/action-tests.go b/integration/action-tests.go index 7e4fce8..6721076 100644 --- a/integration/action-tests.go +++ b/integration/action-tests.go @@ -4,6 +4,14 @@ func TestAuthentication(s *S3Conf) { Authentication_empty_auth_header(s) Authentication_invalid_auth_header(s) Authentication_unsupported_signature_version(s) + Authentication_malformed_credentials(s) + Authentication_malformed_credentials_invalid_parts(s) + Authentication_credentials_terminated_string(s) + Authentication_credentials_incorrect_service(s) + Authentication_credentials_incorrect_region(s) + Authentication_credentials_invalid_date(s) + Authentication_credentials_future_date(s) + Authentication_credentials_past_date(s) } func TestCreateBucket(s *S3Conf) { diff --git a/integration/tests.go b/integration/tests.go index 99d765d..ce9c5f1 100644 --- a/integration/tests.go +++ b/integration/tests.go @@ -3,11 +3,13 @@ package integration import ( "context" "crypto/sha256" + "encoding/xml" "errors" "fmt" "io" "math" "net/http" + "regexp" "strings" "sync" "time" @@ -109,6 +111,287 @@ func Authentication_unsupported_signature_version(s *S3Conf) { }) } +func Authentication_malformed_credentials(s *S3Conf) { + testName := "Authentication_malformed_credentials" + authHandler(s, &authConfig{ + testName: testName, + path: "my-bucket", + method: http.MethodGet, + body: nil, + service: "s3", + date: time.Now(), + }, func(req *http.Request) error { + authHdr := req.Header.Get("Authorization") + regExp := regexp.MustCompile("Credential=[^,]+,") + hdr := regExp.ReplaceAllString(authHdr, "Credential-access/32234/us-east-1/s3/aws4_request,") + req.Header.Set("Authorization", hdr) + + client := http.Client{ + Timeout: shortTimeout, + } + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrCredMalformed)); err != nil { + return err + } + + return nil + }) +} + +func Authentication_malformed_credentials_invalid_parts(s *S3Conf) { + testName := "Authentication_malformed_credentials_invalid_parts" + authHandler(s, &authConfig{ + testName: testName, + path: "my-bucket", + method: http.MethodGet, + body: nil, + service: "s3", + date: time.Now(), + }, func(req *http.Request) error { + authHdr := req.Header.Get("Authorization") + regExp := regexp.MustCompile("Credential=[^,]+,") + hdr := regExp.ReplaceAllString(authHdr, "Credential=access/32234/us-east-1/s3,") + req.Header.Set("Authorization", hdr) + + client := http.Client{ + Timeout: shortTimeout, + } + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrCredMalformed)); err != nil { + return err + } + + return nil + }) +} + +func Authentication_credentials_terminated_string(s *S3Conf) { + testName := "Authentication_credentials_terminated_string" + authHandler(s, &authConfig{ + testName: testName, + path: "my-bucket", + method: http.MethodGet, + body: nil, + service: "s3", + date: time.Now(), + }, func(req *http.Request) error { + authHdr := req.Header.Get("Authorization") + regExp := regexp.MustCompile("Credential=[^,]+,") + hdr := regExp.ReplaceAllString(authHdr, "Credential=access/32234/us-east-1/s3/aws_request,") + req.Header.Set("Authorization", hdr) + + client := http.Client{ + Timeout: shortTimeout, + } + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureTerminationStr)); err != nil { + return err + } + + return nil + }) +} + +func Authentication_credentials_incorrect_service(s *S3Conf) { + testName := "Authentication_credentials_incorrect_service" + authHandler(s, &authConfig{ + testName: testName, + path: "my-bucket", + method: http.MethodGet, + body: nil, + service: "ec2", + date: time.Now(), + }, func(req *http.Request) error { + client := http.Client{ + Timeout: shortTimeout, + } + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureIncorrService)); err != nil { + return err + } + + return nil + }) +} + +func Authentication_credentials_incorrect_region(s *S3Conf) { + testName := "Authentication_credentials_incorrect_region" + cfg := *s + if cfg.awsRegion == "us-east-1" { + cfg.awsRegion = "us-west-1" + } else { + cfg.awsRegion = "us-east-1" + } + authHandler(&cfg, &authConfig{ + testName: testName, + path: "my-bucket", + method: http.MethodGet, + body: nil, + service: "s3", + date: time.Now(), + }, func(req *http.Request) error { + client := http.Client{ + Timeout: shortTimeout, + } + apiErr := s3err.APIError{ + Code: "SignatureDoesNotMatch", + Description: fmt.Sprintf("Credential should be scoped to a valid Region, not %v", cfg.awsRegion), + HTTPStatusCode: http.StatusForbidden, + } + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if err := checkAuthErr(resp, apiErr); err != nil { + return err + } + + return nil + }) +} + +func Authentication_credentials_invalid_date(s *S3Conf) { + testName := "Authentication_credentials_invalid_date" + authHandler(s, &authConfig{ + testName: testName, + path: "my-bucket", + method: http.MethodGet, + body: nil, + service: "s3", + date: time.Now(), + }, func(req *http.Request) error { + authHdr := req.Header.Get("Authorization") + regExp := regexp.MustCompile("Credential=[^,]+,") + hdr := regExp.ReplaceAllString(authHdr, "Credential=access/3223423234/us-east-1/s3/aws4_request,") + req.Header.Set("Authorization", hdr) + + client := http.Client{ + Timeout: shortTimeout, + } + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil { + return err + } + + return nil + }) +} + +func Authentication_credentials_future_date(s *S3Conf) { + testName := "Authentication_credentials_future_date" + authHandler(s, &authConfig{ + testName: testName, + path: "my-bucket", + method: http.MethodGet, + body: nil, + service: "s3", + date: time.Now().Add(time.Duration(5) * 24 * time.Hour), + }, func(req *http.Request) error { + client := http.Client{ + Timeout: shortTimeout, + } + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + var errResp s3err.APIErrorResponse + err = xml.Unmarshal(body, &errResp) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusForbidden { + return fmt.Errorf("expected response status code to be %v, instead got %v", http.StatusForbidden, resp.StatusCode) + } + if errResp.Code != "SignatureDoesNotMatch" { + return fmt.Errorf("expected error code to be %v, instead got %v", "SignatureDoesNotMatch", errResp.Code) + } + if !strings.Contains(errResp.Message, "Signature not yet current:") { + return fmt.Errorf("expected future date error message, instead got %v", errResp.Message) + } + + return nil + }) +} + +func Authentication_credentials_past_date(s *S3Conf) { + testName := "Authentication_credentials_past_date" + authHandler(s, &authConfig{ + testName: testName, + path: "my-bucket", + method: http.MethodGet, + body: nil, + service: "s3", + date: time.Now().Add(time.Duration(-5) * 24 * time.Hour), + }, func(req *http.Request) error { + client := http.Client{ + Timeout: shortTimeout, + } + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + var errResp s3err.APIErrorResponse + err = xml.Unmarshal(body, &errResp) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusForbidden { + return fmt.Errorf("expected response status code to be %v, instead got %v", http.StatusForbidden, resp.StatusCode) + } + if errResp.Code != "SignatureDoesNotMatch" { + return fmt.Errorf("expected error code to be %v, instead got %v", "SignatureDoesNotMatch", errResp.Code) + } + if !strings.Contains(errResp.Message, "Signature expired:") { + return fmt.Errorf("expected past date error message, instead got %v", errResp.Message) + } + + return nil + }) +} + func CreateBucket_invalid_bucket_name(s *S3Conf) { testName := "CreateBucket_invalid_bucket_name" runF(testName) diff --git a/s3api/middlewares/authentication.go b/s3api/middlewares/authentication.go index d0e3488..dc0efe1 100644 --- a/s3api/middlewares/authentication.go +++ b/s3api/middlewares/authentication.go @@ -77,7 +77,7 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, logger s3log.Au } // Credential variables validation creds := strings.Split(credKv[1], "/") - if len(creds) < 5 { + if len(creds) != 5 { return controllers.SendResponse(ctx, s3err.GetAPIError(s3err.ErrCredMalformed), &controllers.MetaOpts{Logger: logger}) } if creds[4] != "aws4_request" {