diff options
Diffstat (limited to 'weed/s3api/auth_signature_v4.go')
| -rw-r--r-- | weed/s3api/auth_signature_v4.go | 135 |
1 files changed, 68 insertions, 67 deletions
diff --git a/weed/s3api/auth_signature_v4.go b/weed/s3api/auth_signature_v4.go index cdfd8be1d..121c793d8 100644 --- a/weed/s3api/auth_signature_v4.go +++ b/weed/s3api/auth_signature_v4.go @@ -23,6 +23,7 @@ import ( "crypto/sha256" "crypto/subtle" "encoding/hex" + "github.com/chrislusf/seaweedfs/weed/s3api/s3err" "net/http" "net/url" "regexp" @@ -33,7 +34,7 @@ import ( "unicode/utf8" ) -func (iam *IdentityAccessManagement) reqSignatureV4Verify(r *http.Request) (*Identity, ErrorCode) { +func (iam *IdentityAccessManagement) reqSignatureV4Verify(r *http.Request) (*Identity, s3err.ErrorCode) { sha256sum := getContentSha256Cksum(r) switch { case isRequestSignatureV4(r): @@ -41,7 +42,7 @@ func (iam *IdentityAccessManagement) reqSignatureV4Verify(r *http.Request) (*Ide case isRequestPresignedSignatureV4(r): return iam.doesPresignedSignatureMatch(sha256sum, r) } - return nil, ErrAccessDenied + return nil, s3err.ErrAccessDenied } // Streaming AWS Signature Version '4' constants. @@ -89,7 +90,7 @@ func getContentSha256Cksum(r *http.Request) string { } // Verify authorization header - http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html -func (iam *IdentityAccessManagement) doesSignatureMatch(hashedPayload string, r *http.Request) (*Identity, ErrorCode) { +func (iam *IdentityAccessManagement) doesSignatureMatch(hashedPayload string, r *http.Request) (*Identity, s3err.ErrorCode) { // Copy request. req := *r @@ -99,33 +100,33 @@ func (iam *IdentityAccessManagement) doesSignatureMatch(hashedPayload string, r // Parse signature version '4' header. signV4Values, err := parseSignV4(v4Auth) - if err != ErrNone { + if err != s3err.ErrNone { return nil, err } // Extract all the signed headers along with its values. extractedSignedHeaders, errCode := extractSignedHeaders(signV4Values.SignedHeaders, r) - if errCode != ErrNone { + if errCode != s3err.ErrNone { return nil, errCode } // Verify if the access key id matches. identity, cred, found := iam.lookupByAccessKey(signV4Values.Credential.accessKey) if !found { - return nil, ErrInvalidAccessKeyID + return nil, s3err.ErrInvalidAccessKeyID } // Extract date, if not present throw error. var date string if date = req.Header.Get(http.CanonicalHeaderKey("X-Amz-Date")); date == "" { if date = r.Header.Get("Date"); date == "" { - return nil, ErrMissingDateHeader + return nil, s3err.ErrMissingDateHeader } } // Parse date header. t, e := time.Parse(iso8601Format, date) if e != nil { - return nil, ErrMalformedDate + return nil, s3err.ErrMalformedDate } // Query string. @@ -145,11 +146,11 @@ func (iam *IdentityAccessManagement) doesSignatureMatch(hashedPayload string, r // Verify if signature match. if !compareSignatureV4(newSignature, signV4Values.Signature) { - return nil, ErrSignatureDoesNotMatch + return nil, s3err.ErrSignatureDoesNotMatch } // Return error none. - return identity, ErrNone + return identity, s3err.ErrNone } // credentialHeader data type represents structured form of Credential @@ -184,65 +185,65 @@ func (c credentialHeader) getScope() string { // Authorization: algorithm Credential=accessKeyID/credScope, \ // SignedHeaders=signedHeaders, Signature=signature // -func parseSignV4(v4Auth string) (sv signValues, aec ErrorCode) { +func parseSignV4(v4Auth string) (sv signValues, aec s3err.ErrorCode) { // Replace all spaced strings, some clients can send spaced // parameters and some won't. So we pro-actively remove any spaces // to make parsing easier. v4Auth = strings.Replace(v4Auth, " ", "", -1) if v4Auth == "" { - return sv, ErrAuthHeaderEmpty + return sv, s3err.ErrAuthHeaderEmpty } // Verify if the header algorithm is supported or not. if !strings.HasPrefix(v4Auth, signV4Algorithm) { - return sv, ErrSignatureVersionNotSupported + return sv, s3err.ErrSignatureVersionNotSupported } // Strip off the Algorithm prefix. v4Auth = strings.TrimPrefix(v4Auth, signV4Algorithm) authFields := strings.Split(strings.TrimSpace(v4Auth), ",") if len(authFields) != 3 { - return sv, ErrMissingFields + return sv, s3err.ErrMissingFields } // Initialize signature version '4' structured header. signV4Values := signValues{} - var err ErrorCode + var err s3err.ErrorCode // Save credentail values. signV4Values.Credential, err = parseCredentialHeader(authFields[0]) - if err != ErrNone { + if err != s3err.ErrNone { return sv, err } // Save signed headers. signV4Values.SignedHeaders, err = parseSignedHeader(authFields[1]) - if err != ErrNone { + if err != s3err.ErrNone { return sv, err } // Save signature. signV4Values.Signature, err = parseSignature(authFields[2]) - if err != ErrNone { + if err != s3err.ErrNone { return sv, err } // Return the structure here. - return signV4Values, ErrNone + return signV4Values, s3err.ErrNone } // parse credentialHeader string into its structured form. -func parseCredentialHeader(credElement string) (ch credentialHeader, aec ErrorCode) { +func parseCredentialHeader(credElement string) (ch credentialHeader, aec s3err.ErrorCode) { creds := strings.Split(strings.TrimSpace(credElement), "=") if len(creds) != 2 { - return ch, ErrMissingFields + return ch, s3err.ErrMissingFields } if creds[0] != "Credential" { - return ch, ErrMissingCredTag + return ch, s3err.ErrMissingCredTag } credElements := strings.Split(strings.TrimSpace(creds[1]), "/") if len(credElements) != 5 { - return ch, ErrCredMalformed + return ch, s3err.ErrCredMalformed } // Save access key id. cred := credentialHeader{ @@ -251,69 +252,69 @@ func parseCredentialHeader(credElement string) (ch credentialHeader, aec ErrorCo var e error cred.scope.date, e = time.Parse(yyyymmdd, credElements[1]) if e != nil { - return ch, ErrMalformedCredentialDate + return ch, s3err.ErrMalformedCredentialDate } cred.scope.region = credElements[2] cred.scope.service = credElements[3] // "s3" cred.scope.request = credElements[4] // "aws4_request" - return cred, ErrNone + return cred, s3err.ErrNone } // Parse slice of signed headers from signed headers tag. -func parseSignedHeader(signedHdrElement string) ([]string, ErrorCode) { +func parseSignedHeader(signedHdrElement string) ([]string, s3err.ErrorCode) { signedHdrFields := strings.Split(strings.TrimSpace(signedHdrElement), "=") if len(signedHdrFields) != 2 { - return nil, ErrMissingFields + return nil, s3err.ErrMissingFields } if signedHdrFields[0] != "SignedHeaders" { - return nil, ErrMissingSignHeadersTag + return nil, s3err.ErrMissingSignHeadersTag } if signedHdrFields[1] == "" { - return nil, ErrMissingFields + return nil, s3err.ErrMissingFields } signedHeaders := strings.Split(signedHdrFields[1], ";") - return signedHeaders, ErrNone + return signedHeaders, s3err.ErrNone } // Parse signature from signature tag. -func parseSignature(signElement string) (string, ErrorCode) { +func parseSignature(signElement string) (string, s3err.ErrorCode) { signFields := strings.Split(strings.TrimSpace(signElement), "=") if len(signFields) != 2 { - return "", ErrMissingFields + return "", s3err.ErrMissingFields } if signFields[0] != "Signature" { - return "", ErrMissingSignTag + return "", s3err.ErrMissingSignTag } if signFields[1] == "" { - return "", ErrMissingFields + return "", s3err.ErrMissingFields } signature := signFields[1] - return signature, ErrNone + return signature, s3err.ErrNone } // check query headers with presigned signature // - http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html -func (iam *IdentityAccessManagement) doesPresignedSignatureMatch(hashedPayload string, r *http.Request) (*Identity, ErrorCode) { +func (iam *IdentityAccessManagement) doesPresignedSignatureMatch(hashedPayload string, r *http.Request) (*Identity, s3err.ErrorCode) { // Copy request req := *r // Parse request query string. pSignValues, err := parsePreSignV4(req.URL.Query()) - if err != ErrNone { + if err != s3err.ErrNone { return nil, err } // Verify if the access key id matches. identity, cred, found := iam.lookupByAccessKey(pSignValues.Credential.accessKey) if !found { - return nil, ErrInvalidAccessKeyID + return nil, s3err.ErrInvalidAccessKeyID } // Extract all the signed headers along with its values. extractedSignedHeaders, errCode := extractSignedHeaders(pSignValues.SignedHeaders, r) - if errCode != ErrNone { + if errCode != s3err.ErrNone { return nil, errCode } // Construct new query. @@ -329,11 +330,11 @@ func (iam *IdentityAccessManagement) doesPresignedSignatureMatch(hashedPayload s // If the host which signed the request is slightly ahead in time (by less than globalMaxSkewTime) the // request should still be allowed. if pSignValues.Date.After(now.Add(15 * time.Minute)) { - return nil, ErrRequestNotReadyYet + return nil, s3err.ErrRequestNotReadyYet } if now.Sub(pSignValues.Date) > pSignValues.Expires { - return nil, ErrExpiredPresignRequest + return nil, s3err.ErrExpiredPresignRequest } // Save the date and expires. @@ -365,24 +366,24 @@ func (iam *IdentityAccessManagement) doesPresignedSignatureMatch(hashedPayload s // Verify if date query is same. if req.URL.Query().Get("X-Amz-Date") != query.Get("X-Amz-Date") { - return nil, ErrSignatureDoesNotMatch + return nil, s3err.ErrSignatureDoesNotMatch } // Verify if expires query is same. if req.URL.Query().Get("X-Amz-Expires") != query.Get("X-Amz-Expires") { - return nil, ErrSignatureDoesNotMatch + return nil, s3err.ErrSignatureDoesNotMatch } // Verify if signed headers query is same. if req.URL.Query().Get("X-Amz-SignedHeaders") != query.Get("X-Amz-SignedHeaders") { - return nil, ErrSignatureDoesNotMatch + return nil, s3err.ErrSignatureDoesNotMatch } // Verify if credential query is same. if req.URL.Query().Get("X-Amz-Credential") != query.Get("X-Amz-Credential") { - return nil, ErrSignatureDoesNotMatch + return nil, s3err.ErrSignatureDoesNotMatch } // Verify if sha256 payload query is same. if req.URL.Query().Get("X-Amz-Content-Sha256") != "" { if req.URL.Query().Get("X-Amz-Content-Sha256") != query.Get("X-Amz-Content-Sha256") { - return nil, ErrContentSHA256Mismatch + return nil, s3err.ErrContentSHA256Mismatch } } @@ -402,9 +403,9 @@ func (iam *IdentityAccessManagement) doesPresignedSignatureMatch(hashedPayload s // Verify signature. if !compareSignatureV4(req.URL.Query().Get("X-Amz-Signature"), newSignature) { - return nil, ErrSignatureDoesNotMatch + return nil, s3err.ErrSignatureDoesNotMatch } - return identity, ErrNone + return identity, s3err.ErrNone } func contains(list []string, elem string) bool { @@ -433,28 +434,28 @@ type preSignValues struct { // querystring += &X-Amz-Signature=signature // // verifies if any of the necessary query params are missing in the presigned request. -func doesV4PresignParamsExist(query url.Values) ErrorCode { +func doesV4PresignParamsExist(query url.Values) s3err.ErrorCode { v4PresignQueryParams := []string{"X-Amz-Algorithm", "X-Amz-Credential", "X-Amz-Signature", "X-Amz-Date", "X-Amz-SignedHeaders", "X-Amz-Expires"} for _, v4PresignQueryParam := range v4PresignQueryParams { if _, ok := query[v4PresignQueryParam]; !ok { - return ErrInvalidQueryParams + return s3err.ErrInvalidQueryParams } } - return ErrNone + return s3err.ErrNone } // Parses all the presigned signature values into separate elements. -func parsePreSignV4(query url.Values) (psv preSignValues, aec ErrorCode) { - var err ErrorCode +func parsePreSignV4(query url.Values) (psv preSignValues, aec s3err.ErrorCode) { + var err s3err.ErrorCode // verify whether the required query params exist. err = doesV4PresignParamsExist(query) - if err != ErrNone { + if err != s3err.ErrNone { return psv, err } // Verify if the query algorithm is supported or not. if query.Get("X-Amz-Algorithm") != signV4Algorithm { - return psv, ErrInvalidQuerySignatureAlgo + return psv, s3err.ErrInvalidQuerySignatureAlgo } // Initialize signature version '4' structured header. @@ -462,7 +463,7 @@ func parsePreSignV4(query url.Values) (psv preSignValues, aec ErrorCode) { // Save credential. preSignV4Values.Credential, err = parseCredentialHeader("Credential=" + query.Get("X-Amz-Credential")) - if err != ErrNone { + if err != s3err.ErrNone { return psv, err } @@ -470,47 +471,47 @@ func parsePreSignV4(query url.Values) (psv preSignValues, aec ErrorCode) { // Save date in native time.Time. preSignV4Values.Date, e = time.Parse(iso8601Format, query.Get("X-Amz-Date")) if e != nil { - return psv, ErrMalformedPresignedDate + return psv, s3err.ErrMalformedPresignedDate } // Save expires in native time.Duration. preSignV4Values.Expires, e = time.ParseDuration(query.Get("X-Amz-Expires") + "s") if e != nil { - return psv, ErrMalformedExpires + return psv, s3err.ErrMalformedExpires } if preSignV4Values.Expires < 0 { - return psv, ErrNegativeExpires + return psv, s3err.ErrNegativeExpires } // Check if Expiry time is less than 7 days (value in seconds). if preSignV4Values.Expires.Seconds() > 604800 { - return psv, ErrMaximumExpires + return psv, s3err.ErrMaximumExpires } // Save signed headers. preSignV4Values.SignedHeaders, err = parseSignedHeader("SignedHeaders=" + query.Get("X-Amz-SignedHeaders")) - if err != ErrNone { + if err != s3err.ErrNone { return psv, err } // Save signature. preSignV4Values.Signature, err = parseSignature("Signature=" + query.Get("X-Amz-Signature")) - if err != ErrNone { + if err != s3err.ErrNone { return psv, err } // Return structed form of signature query string. - return preSignV4Values, ErrNone + return preSignV4Values, s3err.ErrNone } // extractSignedHeaders extract signed headers from Authorization header -func extractSignedHeaders(signedHeaders []string, r *http.Request) (http.Header, ErrorCode) { +func extractSignedHeaders(signedHeaders []string, r *http.Request) (http.Header, s3err.ErrorCode) { reqHeaders := r.Header // find whether "host" is part of list of signed headers. // if not return ErrUnsignedHeaders. "host" is mandatory. if !contains(signedHeaders, "host") { - return nil, ErrUnsignedHeaders + return nil, s3err.ErrUnsignedHeaders } extractedSignedHeaders := make(http.Header) for _, header := range signedHeaders { @@ -555,10 +556,10 @@ func extractSignedHeaders(signedHeaders []string, r *http.Request) (http.Header, // calculation to be compatible with such clients. extractedSignedHeaders.Set(header, strconv.FormatInt(r.ContentLength, 10)) default: - return nil, ErrUnsignedHeaders + return nil, s3err.ErrUnsignedHeaders } } - return extractedSignedHeaders, ErrNone + return extractedSignedHeaders, s3err.ErrNone } // getSignedHeaders generate a string i.e alphabetically sorted, semicolon-separated list of lowercase request header names |
