From 22487b9ccf42ecc91725b8a866a637bd34bbf070 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Tue, 21 Jul 2020 02:43:40 +0300 Subject: [PATCH] Enable auth validation for signed requests --- auth/center.go | 83 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 17 deletions(-) diff --git a/auth/center.go b/auth/center.go index 984162f0..fc629a86 100644 --- a/auth/center.go +++ b/auth/center.go @@ -2,6 +2,7 @@ package auth import ( "bytes" + "context" "crypto/ecdsa" "crypto/rand" "crypto/rsa" @@ -12,7 +13,11 @@ import ( "io/ioutil" "net/http" "regexp" + "strings" + "time" + "github.com/aws/aws-sdk-go/aws/credentials" + v4 "github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/klauspost/compress/zstd" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" @@ -107,50 +112,94 @@ func (center *Center) packBearerToken(bearerToken *service.BearerTokenMsg) (stri return accessKeyID, secretAccessKey, nil } -func (center *Center) unpackBearerToken(accessKeyID string) (*service.BearerTokenMsg, error) { +func (center *Center) unpackBearerToken(accessKeyID string) (*service.BearerTokenMsg, string, error) { encryptedKeyID, err := hex.DecodeString(accessKeyID) if err != nil { - return nil, errors.Wrap(err, "failed to decode HEX string") + return nil, "", errors.Wrap(err, "failed to decode HEX string") } compressedKeyID, err := decrypt(center.userAuthKeys.PrivateKey, encryptedKeyID) if err != nil { - return nil, errors.Wrap(err, "failed to decrypt key ID") + return nil, "", errors.Wrap(err, "failed to decrypt key ID") } data, err := center.decompress(compressedKeyID) if err != nil { - return nil, errors.Wrap(err, "failed to decompress key ID") + return nil, "", errors.Wrap(err, "failed to decompress key ID") } bearerToken := new(service.BearerTokenMsg) if err := bearerToken.Unmarshal(data); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal embedded bearer token") + return nil, "", errors.Wrap(err, "failed to unmarshal embedded bearer token") } - return bearerToken, nil + secretAccessKey := hex.EncodeToString(sha256Hash(data)) + return bearerToken, secretAccessKey, nil } func (center *Center) AuthenticationPassed(request *http.Request) (*service.BearerTokenMsg, error) { + queryValues := request.URL.Query() + if queryValues.Get("X-Amz-Algorithm") == "AWS4-HMAC-SHA256" { + return nil, errors.New("pre-signed form of request is not supported") + } authHeaderField := request.Header["Authorization"] if len(authHeaderField) != 1 { return nil, errors.New("unsupported request: wrong length of Authorization header field") } - sms := center.submatcher.getSubmatches(authHeaderField[0]) - if len(sms) != 6 { + sms1 := center.submatcher.getSubmatches(authHeaderField[0]) + if len(sms1) != 6 { return nil, errors.New("bad Authorization header field") } - bt, err := center.unpackBearerToken(sms["access_key_id"]) - if err != nil { - center.log.Warn("Failed to unpack bearer token", zap.Error(err)) - //return nil, errors.Wrap(err, "failed to unpack bearer token") + signedHeaderFieldsNames := strings.Split(sms1["signed_header_fields"], ";") + if len(signedHeaderFieldsNames) == 0 { + return nil, errors.New("wrong format of signed headers part") } - return bt, nil + signatureDateTime, err := time.Parse("20060102T150405Z", request.Header.Get("X-Amz-Date")) + if err != nil { + // TODO + } + accessKeyID := sms1["access_key_id"] + bearerToken, secretAccessKey, err := center.unpackBearerToken(accessKeyID) + if err != nil { + // FIXME: Should be `return nil, errors.Wrap(err, "failed to unpack bearer token")` + center.log.Warn("Failed to unpack bearer token", zap.Error(err)) + return nil, nil + } + otherRequest := request.Clone(context.TODO()) + otherRequest.Header = map[string][]string{} + for hfn, hfvs := range request.Header { + for _, shfn := range signedHeaderFieldsNames { + if strings.EqualFold(hfn, shfn) { + otherRequest.Header[hfn] = hfvs + } + } + } + awsCreds := credentials.NewStaticCredentials(accessKeyID, secretAccessKey, "") + signer := v4.NewSigner(awsCreds) + body, err := readAndKeepBody(request) + if err != nil { + // TODO + } + _, err = signer.Sign(otherRequest, body, sms1["service"], sms1["region"], signatureDateTime) + if err != nil { + // TODO + } + sms2 := center.submatcher.getSubmatches(otherRequest.Header.Get("Authorization")) + if sms1["v4_signature"] != sms2["v4_signature"] { + // FIXME: Should be `return nil, errors.Wrap(err, "failed to pass authentication procedure")` + center.log.Warn("Failed to pass authentication procedure") + return nil, nil + } + return bearerToken, nil } -func readAndReplaceBody(request *http.Request) []byte { +func readAndKeepBody(request *http.Request) (*bytes.Reader, error) { if request.Body == nil { - return []byte{} + var r bytes.Reader + return &r, nil + } + payload, err := ioutil.ReadAll(request.Body) + if err != nil { + return nil, err } - payload, _ := ioutil.ReadAll(request.Body) request.Body = ioutil.NopCloser(bytes.NewReader(payload)) - return payload + return bytes.NewReader(payload), nil } func (center *Center) compress(data []byte) []byte {