mirror of
https://github.com/pgsty/minio.git
synced 2026-07-21 05:00:22 +03:00
handler/PUT: Handle signature verification through a custom reader. (#2066)
Change brings in a new signVerifyReader which provides a io.Reader compatible reader, additionally implements Verify() function. Verify() function validates the signature present in the incoming request. This approach is choosen to avoid complexities involved in using io.Pipe(). Thanks to Krishna for his inputs on this. Fixes #2058 Fixes #2054 Fixes #2087
This commit is contained in:
committed by
Anand Babu (AB) Periasamy
parent
0540863663
commit
8a028a9efb
+10
-148
@@ -17,10 +17,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@@ -28,7 +26,6 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
mux "github.com/gorilla/mux"
|
||||
@@ -51,14 +48,6 @@ func setGetRespHeaders(w http.ResponseWriter, reqParams url.Values) {
|
||||
}
|
||||
}
|
||||
|
||||
// http Header "x-amz-content-sha256" == "UNSIGNED-PAYLOAD" indicates that the
|
||||
// client did not calculate sha256 of the payload. Hence we skip calculating sha256.
|
||||
// We also skip calculating sha256 for presigned requests without "x-amz-content-sha256" header.
|
||||
func skipSHA256Calculation(r *http.Request) bool {
|
||||
shaHeader := r.Header.Get("X-Amz-Content-Sha256")
|
||||
return isRequestUnsignedPayload(r) || (isRequestPresignedSignatureV4(r) && shaHeader == "")
|
||||
}
|
||||
|
||||
// errAllowableNotFound - For an anon user, return 404 if have ListBucket, 403 otherwise
|
||||
// this is in keeping with the permissions sections of the docs of both:
|
||||
// HEAD Object: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectHEAD.html
|
||||
@@ -604,73 +593,10 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||
// Create anonymous object.
|
||||
md5Sum, err = api.ObjectAPI.PutObject(bucket, object, size, r.Body, metadata)
|
||||
case authTypePresigned, authTypeSigned:
|
||||
validateRegion := true // Validate region.
|
||||
|
||||
if skipSHA256Calculation(r) {
|
||||
// Either sha256-header is "UNSIGNED-PAYLOAD" or this is a presigned PUT
|
||||
// request without sha256-header.
|
||||
var s3Error APIErrorCode
|
||||
if isRequestSignatureV4(r) {
|
||||
s3Error = doesSignatureMatch(unsignedPayload, r, validateRegion)
|
||||
} else if isRequestPresignedSignatureV4(r) {
|
||||
s3Error = doesPresignedSignatureMatch(unsignedPayload, r, validateRegion)
|
||||
}
|
||||
if s3Error != ErrNone {
|
||||
if s3Error == ErrSignatureDoesNotMatch {
|
||||
err = errSignatureMismatch
|
||||
} else {
|
||||
err = fmt.Errorf("%v", getAPIError(s3Error))
|
||||
}
|
||||
} else {
|
||||
md5Sum, err = api.ObjectAPI.PutObject(bucket, object, size, r.Body, metadata)
|
||||
}
|
||||
} else {
|
||||
// Sha256 of payload has to be calculated and matched with what was sent in the header.
|
||||
// Initialize a pipe for data pipe line.
|
||||
reader, writer := io.Pipe()
|
||||
var wg = &sync.WaitGroup{}
|
||||
// Start writing in a routine.
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
shaWriter := sha256.New()
|
||||
multiWriter := io.MultiWriter(shaWriter, writer)
|
||||
if _, wErr := io.CopyN(multiWriter, r.Body, size); wErr != nil {
|
||||
// Pipe closed.
|
||||
if wErr == io.ErrClosedPipe {
|
||||
return
|
||||
}
|
||||
errorIf(wErr, "Unable to read from HTTP body.")
|
||||
writer.CloseWithError(wErr)
|
||||
return
|
||||
}
|
||||
shaPayload := shaWriter.Sum(nil)
|
||||
var s3Error APIErrorCode
|
||||
if isRequestSignatureV4(r) {
|
||||
s3Error = doesSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
||||
} else if isRequestPresignedSignatureV4(r) {
|
||||
s3Error = doesPresignedSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
||||
}
|
||||
var sErr error
|
||||
if s3Error != ErrNone {
|
||||
if s3Error == ErrSignatureDoesNotMatch {
|
||||
sErr = errSignatureMismatch
|
||||
} else {
|
||||
sErr = fmt.Errorf("%v", getAPIError(s3Error))
|
||||
}
|
||||
writer.CloseWithError(sErr)
|
||||
return
|
||||
}
|
||||
writer.Close()
|
||||
}()
|
||||
|
||||
// Create object.
|
||||
md5Sum, err = api.ObjectAPI.PutObject(bucket, object, size, reader, metadata)
|
||||
// Close the pipe.
|
||||
reader.Close()
|
||||
// Wait for all the routines to finish.
|
||||
wg.Wait()
|
||||
}
|
||||
// Initialize signature verifier.
|
||||
reader := newSignVerify(r)
|
||||
// Create object.
|
||||
md5Sum, err = api.ObjectAPI.PutObject(bucket, object, size, reader, metadata)
|
||||
}
|
||||
if err != nil {
|
||||
errorIf(err, "Unable to create an object.")
|
||||
@@ -781,6 +707,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
||||
}
|
||||
|
||||
var partMD5 string
|
||||
incomingMD5 := hex.EncodeToString(md5Bytes)
|
||||
switch getRequestAuthType(r) {
|
||||
default:
|
||||
// For all unknown auth types return error.
|
||||
@@ -792,77 +719,12 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||
return
|
||||
}
|
||||
// No need to verify signature, anonymous request access is
|
||||
// already allowed.
|
||||
hexMD5 := hex.EncodeToString(md5Bytes)
|
||||
partMD5, err = api.ObjectAPI.PutObjectPart(bucket, object, uploadID, partID, size, r.Body, hexMD5)
|
||||
// No need to verify signature, anonymous request access is already allowed.
|
||||
partMD5, err = api.ObjectAPI.PutObjectPart(bucket, object, uploadID, partID, size, r.Body, incomingMD5)
|
||||
case authTypePresigned, authTypeSigned:
|
||||
validateRegion := true // Validate region.
|
||||
|
||||
if skipSHA256Calculation(r) {
|
||||
// Either sha256-header is "UNSIGNED-PAYLOAD" or this is a presigned
|
||||
// request without sha256-header.
|
||||
var s3Error APIErrorCode
|
||||
if isRequestSignatureV4(r) {
|
||||
s3Error = doesSignatureMatch(unsignedPayload, r, validateRegion)
|
||||
} else if isRequestPresignedSignatureV4(r) {
|
||||
s3Error = doesPresignedSignatureMatch(unsignedPayload, r, validateRegion)
|
||||
}
|
||||
if s3Error != ErrNone {
|
||||
if s3Error == ErrSignatureDoesNotMatch {
|
||||
err = errSignatureMismatch
|
||||
} else {
|
||||
err = fmt.Errorf("%v", getAPIError(s3Error))
|
||||
}
|
||||
} else {
|
||||
md5SumHex := hex.EncodeToString(md5Bytes)
|
||||
partMD5, err = api.ObjectAPI.PutObjectPart(bucket, object, uploadID, partID, size, r.Body, md5SumHex)
|
||||
}
|
||||
} else {
|
||||
// Initialize a pipe for data pipe line.
|
||||
reader, writer := io.Pipe()
|
||||
var wg = &sync.WaitGroup{}
|
||||
// Start writing in a routine.
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
shaWriter := sha256.New()
|
||||
multiWriter := io.MultiWriter(shaWriter, writer)
|
||||
if _, wErr := io.CopyN(multiWriter, r.Body, size); wErr != nil {
|
||||
// Pipe closed, just ignore it.
|
||||
if wErr == io.ErrClosedPipe {
|
||||
return
|
||||
}
|
||||
errorIf(wErr, "Unable to read from HTTP request body.")
|
||||
writer.CloseWithError(wErr)
|
||||
return
|
||||
}
|
||||
shaPayload := shaWriter.Sum(nil)
|
||||
var s3Error APIErrorCode
|
||||
if isRequestSignatureV4(r) {
|
||||
s3Error = doesSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
||||
} else if isRequestPresignedSignatureV4(r) {
|
||||
s3Error = doesPresignedSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
||||
}
|
||||
if s3Error != ErrNone {
|
||||
if s3Error == ErrSignatureDoesNotMatch {
|
||||
err = errSignatureMismatch
|
||||
} else {
|
||||
err = fmt.Errorf("%v", getAPIError(s3Error))
|
||||
}
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
// Close the writer.
|
||||
writer.Close()
|
||||
}()
|
||||
md5SumHex := hex.EncodeToString(md5Bytes)
|
||||
partMD5, err = api.ObjectAPI.PutObjectPart(bucket, object, uploadID, partID, size, reader, md5SumHex)
|
||||
// Close the pipe.
|
||||
reader.Close()
|
||||
// Wait for all the routines to finish.
|
||||
wg.Wait()
|
||||
}
|
||||
// Initialize signature verifier.
|
||||
reader := newSignVerify(r)
|
||||
partMD5, err = api.ObjectAPI.PutObjectPart(bucket, object, uploadID, partID, size, reader, incomingMD5)
|
||||
}
|
||||
if err != nil {
|
||||
errorIf(err, "Unable to create object part.")
|
||||
|
||||
Reference in New Issue
Block a user