You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
7.6 KiB

  1. /*
  2. * Minio Cloud Storage, (C) 2015, 2016 Minio, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package cmd
  17. import (
  18. "bytes"
  19. "crypto/md5"
  20. "crypto/sha256"
  21. "encoding/base64"
  22. "encoding/hex"
  23. "io/ioutil"
  24. "net/http"
  25. "strings"
  26. )
  27. // Verify if the request http Header "x-amz-content-sha256" == "UNSIGNED-PAYLOAD"
  28. func isRequestUnsignedPayload(r *http.Request) bool {
  29. return r.Header.Get("x-amz-content-sha256") == unsignedPayload
  30. }
  31. // Verify if request has JWT.
  32. func isRequestJWT(r *http.Request) bool {
  33. return strings.HasPrefix(r.Header.Get("Authorization"), jwtAlgorithm)
  34. }
  35. // Verify if request has AWS Signature Version '4'.
  36. func isRequestSignatureV4(r *http.Request) bool {
  37. return strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm)
  38. }
  39. // Verify if request has AWS Signature Version '2'.
  40. func isRequestSignatureV2(r *http.Request) bool {
  41. return (!strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm) &&
  42. strings.HasPrefix(r.Header.Get("Authorization"), signV2Algorithm))
  43. }
  44. // Verify if request has AWS PreSign Version '4'.
  45. func isRequestPresignedSignatureV4(r *http.Request) bool {
  46. _, ok := r.URL.Query()["X-Amz-Credential"]
  47. return ok
  48. }
  49. // Verify request has AWS PreSign Version '2'.
  50. func isRequestPresignedSignatureV2(r *http.Request) bool {
  51. _, ok := r.URL.Query()["AWSAccessKeyId"]
  52. return ok
  53. }
  54. // Verify if request has AWS Post policy Signature Version '4'.
  55. func isRequestPostPolicySignatureV4(r *http.Request) bool {
  56. return strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") && r.Method == "POST"
  57. }
  58. // Verify if the request has AWS Streaming Signature Version '4'. This is only valid for 'PUT' operation.
  59. func isRequestSignStreamingV4(r *http.Request) bool {
  60. return r.Header.Get("x-amz-content-sha256") == streamingContentSHA256 && r.Method == "PUT"
  61. }
  62. // Authorization type.
  63. type authType int
  64. // List of all supported auth types.
  65. const (
  66. authTypeUnknown authType = iota
  67. authTypeAnonymous
  68. authTypePresigned
  69. authTypePresignedV2
  70. authTypePostPolicy
  71. authTypeStreamingSigned
  72. authTypeSigned
  73. authTypeSignedV2
  74. authTypeJWT
  75. )
  76. // Get request authentication type.
  77. func getRequestAuthType(r *http.Request) authType {
  78. if isRequestSignatureV2(r) {
  79. return authTypeSignedV2
  80. } else if isRequestPresignedSignatureV2(r) {
  81. return authTypePresignedV2
  82. } else if isRequestSignStreamingV4(r) {
  83. return authTypeStreamingSigned
  84. } else if isRequestSignatureV4(r) {
  85. return authTypeSigned
  86. } else if isRequestPresignedSignatureV4(r) {
  87. return authTypePresigned
  88. } else if isRequestJWT(r) {
  89. return authTypeJWT
  90. } else if isRequestPostPolicySignatureV4(r) {
  91. return authTypePostPolicy
  92. } else if _, ok := r.Header["Authorization"]; !ok {
  93. return authTypeAnonymous
  94. }
  95. return authTypeUnknown
  96. }
  97. // sum256 calculate sha256 sum for an input byte array
  98. func sum256(data []byte) []byte {
  99. hash := sha256.New()
  100. hash.Write(data)
  101. return hash.Sum(nil)
  102. }
  103. // sumMD5 calculate md5 sum for an input byte array
  104. func sumMD5(data []byte) []byte {
  105. hash := md5.New()
  106. hash.Write(data)
  107. return hash.Sum(nil)
  108. }
  109. // Verify if request has valid AWS Signature Version '2'.
  110. func isReqAuthenticatedV2(r *http.Request) (s3Error APIErrorCode) {
  111. if isRequestSignatureV2(r) {
  112. return doesSignV2Match(r)
  113. }
  114. return doesPresignV2SignatureMatch(r)
  115. }
  116. func reqSignatureV4Verify(r *http.Request) (s3Error APIErrorCode) {
  117. sha256sum := r.Header.Get("X-Amz-Content-Sha256")
  118. // Skips calculating sha256 on the payload on server,
  119. // if client requested for it.
  120. if skipContentSha256Cksum(r) {
  121. sha256sum = unsignedPayload
  122. }
  123. if isRequestSignatureV4(r) {
  124. return doesSignatureMatch(sha256sum, r, serverConfig.GetRegion())
  125. } else if isRequestPresignedSignatureV4(r) {
  126. return doesPresignedSignatureMatch(sha256sum, r, serverConfig.GetRegion())
  127. }
  128. return ErrAccessDenied
  129. }
  130. // Verify if request has valid AWS Signature Version '4'.
  131. func isReqAuthenticated(r *http.Request, region string) (s3Error APIErrorCode) {
  132. if r == nil {
  133. return ErrInternalError
  134. }
  135. payload, err := ioutil.ReadAll(r.Body)
  136. if err != nil {
  137. errorIf(err, "Unable to read request body for signature verification")
  138. return ErrInternalError
  139. }
  140. // Verify Content-Md5, if payload is set.
  141. if r.Header.Get("Content-Md5") != "" {
  142. if r.Header.Get("Content-Md5") != base64.StdEncoding.EncodeToString(sumMD5(payload)) {
  143. return ErrBadDigest
  144. }
  145. }
  146. // Populate back the payload.
  147. r.Body = ioutil.NopCloser(bytes.NewReader(payload))
  148. var sha256sum string
  149. // Skips calculating sha256 on the payload on server,
  150. // if client requested for it.
  151. if skipContentSha256Cksum(r) {
  152. sha256sum = unsignedPayload
  153. } else {
  154. sha256sum = hex.EncodeToString(sum256(payload))
  155. }
  156. if isRequestSignatureV4(r) {
  157. return doesSignatureMatch(sha256sum, r, region)
  158. } else if isRequestPresignedSignatureV4(r) {
  159. return doesPresignedSignatureMatch(sha256sum, r, region)
  160. }
  161. return ErrAccessDenied
  162. }
  163. // checkAuth - checks for conditions satisfying the authorization of
  164. // the incoming request. Request should be either Presigned or Signed
  165. // in accordance with AWS S3 Signature V4 requirements. ErrAccessDenied
  166. // is returned for unhandled auth type. Once the auth type is indentified
  167. // request headers and body are used to calculate the signature validating
  168. // the client signature present in request.
  169. func checkAuth(r *http.Request) APIErrorCode {
  170. return checkAuthWithRegion(r, serverConfig.GetRegion())
  171. }
  172. // checkAuthWithRegion - similar to checkAuth but takes a custom region.
  173. func checkAuthWithRegion(r *http.Request, region string) APIErrorCode {
  174. // Validates the request for both Presigned and Signed.
  175. aType := getRequestAuthType(r)
  176. switch aType {
  177. case authTypeSignedV2, authTypePresignedV2: // Signature V2.
  178. return isReqAuthenticatedV2(r)
  179. case authTypeSigned, authTypePresigned: // Signature V4.
  180. return isReqAuthenticated(r, region)
  181. }
  182. // For all unhandled auth types return error AccessDenied.
  183. return ErrAccessDenied
  184. }
  185. // authHandler - handles all the incoming authorization headers and validates them if possible.
  186. type authHandler struct {
  187. handler http.Handler
  188. }
  189. // setAuthHandler to validate authorization header for the incoming request.
  190. func setAuthHandler(h http.Handler) http.Handler {
  191. return authHandler{h}
  192. }
  193. // List of all support S3 auth types.
  194. var supportedS3AuthTypes = map[authType]struct{}{
  195. authTypeAnonymous: {},
  196. authTypePresigned: {},
  197. authTypePresignedV2: {},
  198. authTypeSigned: {},
  199. authTypeSignedV2: {},
  200. authTypePostPolicy: {},
  201. authTypeStreamingSigned: {},
  202. }
  203. // Validate if the authType is valid and supported.
  204. func isSupportedS3AuthType(aType authType) bool {
  205. _, ok := supportedS3AuthTypes[aType]
  206. return ok
  207. }
  208. // handler for validating incoming authorization headers.
  209. func (a authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  210. aType := getRequestAuthType(r)
  211. if isSupportedS3AuthType(aType) {
  212. // Let top level caller validate for anonymous and known signed requests.
  213. a.handler.ServeHTTP(w, r)
  214. return
  215. } else if aType == authTypeJWT {
  216. // Validate Authorization header if its valid for JWT request.
  217. if !isJWTReqAuthenticated(r) {
  218. w.WriteHeader(http.StatusUnauthorized)
  219. return
  220. }
  221. a.handler.ServeHTTP(w, r)
  222. return
  223. }
  224. writeErrorResponse(w, r, ErrSignatureVersionNotSupported, r.URL.Path)
  225. }