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

4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. // Copyright 2019 Huawei Technologies Co.,Ltd.
  2. // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  3. // this file except in compliance with the License. You may obtain a copy of the
  4. // License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software distributed
  9. // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  10. // CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. // specific language governing permissions and limitations under the License.
  12. package obs
  13. import (
  14. "fmt"
  15. "net/url"
  16. "sort"
  17. "strings"
  18. "time"
  19. )
  20. func (obsClient ObsClient) doAuthTemporary(method, bucketName, objectKey string, params map[string]string,
  21. headers map[string][]string, expires int64) (requestURL string, err error) {
  22. isAkSkEmpty := obsClient.conf.securityProvider == nil || obsClient.conf.securityProvider.ak == "" || obsClient.conf.securityProvider.sk == ""
  23. if isAkSkEmpty == false && obsClient.conf.securityProvider.securityToken != "" {
  24. if obsClient.conf.signature == SignatureObs {
  25. params[HEADER_STS_TOKEN_OBS] = obsClient.conf.securityProvider.securityToken
  26. } else {
  27. params[HEADER_STS_TOKEN_AMZ] = obsClient.conf.securityProvider.securityToken
  28. }
  29. }
  30. requestURL, canonicalizedURL := obsClient.conf.formatUrls(bucketName, objectKey, params, true)
  31. parsedRequestURL, err := url.Parse(requestURL)
  32. if err != nil {
  33. return "", err
  34. }
  35. encodeHeaders(headers)
  36. hostName := parsedRequestURL.Host
  37. isV4 := obsClient.conf.signature == SignatureV4
  38. prepareHostAndDate(headers, hostName, isV4)
  39. if isAkSkEmpty {
  40. doLog(LEVEL_WARN, "No ak/sk provided, skip to construct authorization")
  41. } else {
  42. if isV4 {
  43. date, parseDateErr := time.Parse(RFC1123_FORMAT, headers[HEADER_DATE_CAMEL][0])
  44. if parseDateErr != nil {
  45. doLog(LEVEL_WARN, "Failed to parse date with reason: %v", parseDateErr)
  46. return "", parseDateErr
  47. }
  48. delete(headers, HEADER_DATE_CAMEL)
  49. shortDate := date.Format(SHORT_DATE_FORMAT)
  50. longDate := date.Format(LONG_DATE_FORMAT)
  51. if len(headers[HEADER_HOST_CAMEL]) != 0 {
  52. index := strings.LastIndex(headers[HEADER_HOST_CAMEL][0], ":")
  53. if index != -1 {
  54. port := headers[HEADER_HOST_CAMEL][0][index+1:]
  55. if port == "80" || port == "443" {
  56. headers[HEADER_HOST_CAMEL] = []string{headers[HEADER_HOST_CAMEL][0][:index]}
  57. }
  58. }
  59. }
  60. signedHeaders, _headers := getSignedHeaders(headers)
  61. credential, scope := getCredential(obsClient.conf.securityProvider.ak, obsClient.conf.region, shortDate)
  62. params[PARAM_ALGORITHM_AMZ_CAMEL] = V4_HASH_PREFIX
  63. params[PARAM_CREDENTIAL_AMZ_CAMEL] = credential
  64. params[PARAM_DATE_AMZ_CAMEL] = longDate
  65. params[PARAM_EXPIRES_AMZ_CAMEL] = Int64ToString(expires)
  66. params[PARAM_SIGNEDHEADERS_AMZ_CAMEL] = strings.Join(signedHeaders, ";")
  67. requestURL, canonicalizedURL = obsClient.conf.formatUrls(bucketName, objectKey, params, true)
  68. parsedRequestURL, _err := url.Parse(requestURL)
  69. if _err != nil {
  70. return "", _err
  71. }
  72. stringToSign := getV4StringToSign(method, canonicalizedURL, parsedRequestURL.RawQuery, scope, longDate, UNSIGNED_PAYLOAD, signedHeaders, _headers)
  73. signature := getSignature(stringToSign, obsClient.conf.securityProvider.sk, obsClient.conf.region, shortDate)
  74. requestURL += fmt.Sprintf("&%s=%s", PARAM_SIGNATURE_AMZ_CAMEL, UrlEncode(signature, false))
  75. } else {
  76. originDate := headers[HEADER_DATE_CAMEL][0]
  77. date, parseDateErr := time.Parse(RFC1123_FORMAT, originDate)
  78. if parseDateErr != nil {
  79. doLog(LEVEL_WARN, "Failed to parse date with reason: %v", parseDateErr)
  80. return "", parseDateErr
  81. }
  82. expires += date.Unix()
  83. headers[HEADER_DATE_CAMEL] = []string{Int64ToString(expires)}
  84. stringToSign := getV2StringToSign(method, canonicalizedURL, headers, obsClient.conf.signature == SignatureObs)
  85. signature := UrlEncode(Base64Encode(HmacSha1([]byte(obsClient.conf.securityProvider.sk), []byte(stringToSign))), false)
  86. if strings.Index(requestURL, "?") < 0 {
  87. requestURL += "?"
  88. } else {
  89. requestURL += "&"
  90. }
  91. delete(headers, HEADER_DATE_CAMEL)
  92. if obsClient.conf.signature != SignatureObs {
  93. requestURL += "AWS"
  94. }
  95. requestURL += fmt.Sprintf("AccessKeyId=%s&Expires=%d&Signature=%s", UrlEncode(obsClient.conf.securityProvider.ak, false), expires, signature)
  96. }
  97. }
  98. return
  99. }
  100. func (obsClient ObsClient) doAuth(method, bucketName, objectKey string, params map[string]string,
  101. headers map[string][]string, hostName string) (requestURL string, err error) {
  102. isAkSkEmpty := obsClient.conf.securityProvider == nil || obsClient.conf.securityProvider.ak == "" || obsClient.conf.securityProvider.sk == ""
  103. if isAkSkEmpty == false && obsClient.conf.securityProvider.securityToken != "" {
  104. if obsClient.conf.signature == SignatureObs {
  105. headers[HEADER_STS_TOKEN_OBS] = []string{obsClient.conf.securityProvider.securityToken}
  106. } else {
  107. headers[HEADER_STS_TOKEN_AMZ] = []string{obsClient.conf.securityProvider.securityToken}
  108. }
  109. }
  110. isObs := obsClient.conf.signature == SignatureObs
  111. requestURL, canonicalizedURL := obsClient.conf.formatUrls(bucketName, objectKey, params, true)
  112. parsedRequestURL, err := url.Parse(requestURL)
  113. if err != nil {
  114. return "", err
  115. }
  116. encodeHeaders(headers)
  117. if hostName == "" {
  118. hostName = parsedRequestURL.Host
  119. }
  120. isV4 := obsClient.conf.signature == SignatureV4
  121. prepareHostAndDate(headers, hostName, isV4)
  122. if isAkSkEmpty {
  123. doLog(LEVEL_WARN, "No ak/sk provided, skip to construct authorization")
  124. } else {
  125. ak := obsClient.conf.securityProvider.ak
  126. sk := obsClient.conf.securityProvider.sk
  127. var authorization string
  128. if isV4 {
  129. headers[HEADER_CONTENT_SHA256_AMZ] = []string{UNSIGNED_PAYLOAD}
  130. ret := v4Auth(ak, sk, obsClient.conf.region, method, canonicalizedURL, parsedRequestURL.RawQuery, headers)
  131. authorization = fmt.Sprintf("%s Credential=%s,SignedHeaders=%s,Signature=%s", V4_HASH_PREFIX, ret["Credential"], ret["SignedHeaders"], ret["Signature"])
  132. } else {
  133. ret := v2Auth(ak, sk, method, canonicalizedURL, headers, isObs)
  134. hashPrefix := V2_HASH_PREFIX
  135. if isObs {
  136. hashPrefix = OBS_HASH_PREFIX
  137. }
  138. authorization = fmt.Sprintf("%s %s:%s", hashPrefix, ak, ret["Signature"])
  139. }
  140. headers[HEADER_AUTH_CAMEL] = []string{authorization}
  141. }
  142. return
  143. }
  144. func prepareHostAndDate(headers map[string][]string, hostName string, isV4 bool) {
  145. headers[HEADER_HOST_CAMEL] = []string{hostName}
  146. if date, ok := headers[HEADER_DATE_AMZ]; ok {
  147. flag := false
  148. if len(date) == 1 {
  149. if isV4 {
  150. if t, err := time.Parse(LONG_DATE_FORMAT, date[0]); err == nil {
  151. headers[HEADER_DATE_CAMEL] = []string{FormatUtcToRfc1123(t)}
  152. flag = true
  153. }
  154. } else {
  155. if strings.HasSuffix(date[0], "GMT") {
  156. headers[HEADER_DATE_CAMEL] = []string{date[0]}
  157. flag = true
  158. }
  159. }
  160. }
  161. if !flag {
  162. delete(headers, HEADER_DATE_AMZ)
  163. }
  164. }
  165. if _, ok := headers[HEADER_DATE_CAMEL]; !ok {
  166. headers[HEADER_DATE_CAMEL] = []string{FormatUtcToRfc1123(time.Now().UTC())}
  167. }
  168. }
  169. func encodeHeaders(headers map[string][]string) {
  170. for key, values := range headers {
  171. for index, value := range values {
  172. values[index] = UrlEncode(value, true)
  173. }
  174. headers[key] = values
  175. }
  176. }
  177. func attachHeaders(headers map[string][]string, isObs bool) string {
  178. length := len(headers)
  179. _headers := make(map[string][]string, length)
  180. keys := make([]string, 0, length)
  181. for key, value := range headers {
  182. _key := strings.ToLower(strings.TrimSpace(key))
  183. if _key != "" {
  184. prefixheader := HEADER_PREFIX
  185. if isObs {
  186. prefixheader = HEADER_PREFIX_OBS
  187. }
  188. if _key == "content-md5" || _key == "content-type" || _key == "date" || strings.HasPrefix(_key, prefixheader) {
  189. keys = append(keys, _key)
  190. _headers[_key] = value
  191. }
  192. } else {
  193. delete(headers, key)
  194. }
  195. }
  196. for _, interestedHeader := range interestedHeaders {
  197. if _, ok := _headers[interestedHeader]; !ok {
  198. _headers[interestedHeader] = []string{""}
  199. keys = append(keys, interestedHeader)
  200. }
  201. }
  202. dateCamelHeader := PARAM_DATE_AMZ_CAMEL
  203. dataHeader := HEADER_DATE_AMZ
  204. if isObs {
  205. dateCamelHeader = PARAM_DATE_OBS_CAMEL
  206. dataHeader = HEADER_DATE_OBS
  207. }
  208. if _, ok := _headers[HEADER_DATE_CAMEL]; ok {
  209. if _, ok := _headers[dataHeader]; ok {
  210. _headers[HEADER_DATE_CAMEL] = []string{""}
  211. } else if _, ok := headers[dateCamelHeader]; ok {
  212. _headers[HEADER_DATE_CAMEL] = []string{""}
  213. }
  214. } else if _, ok := _headers[strings.ToLower(HEADER_DATE_CAMEL)]; ok {
  215. if _, ok := _headers[dataHeader]; ok {
  216. _headers[HEADER_DATE_CAMEL] = []string{""}
  217. } else if _, ok := headers[dateCamelHeader]; ok {
  218. _headers[HEADER_DATE_CAMEL] = []string{""}
  219. }
  220. }
  221. sort.Strings(keys)
  222. stringToSign := make([]string, 0, len(keys))
  223. for _, key := range keys {
  224. var value string
  225. prefixHeader := HEADER_PREFIX
  226. prefixMetaHeader := HEADER_PREFIX_META
  227. if isObs {
  228. prefixHeader = HEADER_PREFIX_OBS
  229. prefixMetaHeader = HEADER_PREFIX_META_OBS
  230. }
  231. if strings.HasPrefix(key, prefixHeader) {
  232. if strings.HasPrefix(key, prefixMetaHeader) {
  233. for index, v := range _headers[key] {
  234. value += strings.TrimSpace(v)
  235. if index != len(_headers[key])-1 {
  236. value += ","
  237. }
  238. }
  239. } else {
  240. value = strings.Join(_headers[key], ",")
  241. }
  242. value = fmt.Sprintf("%s:%s", key, value)
  243. } else {
  244. value = strings.Join(_headers[key], ",")
  245. }
  246. stringToSign = append(stringToSign, value)
  247. }
  248. return strings.Join(stringToSign, "\n")
  249. }
  250. func getV2StringToSign(method, canonicalizedURL string, headers map[string][]string, isObs bool) string {
  251. stringToSign := strings.Join([]string{method, "\n", attachHeaders(headers, isObs), "\n", canonicalizedURL}, "")
  252. var isSecurityToken bool
  253. var securityToken []string
  254. if isObs {
  255. securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_OBS]
  256. } else {
  257. securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_AMZ]
  258. }
  259. var query []string
  260. if !isSecurityToken {
  261. parmas := strings.Split(canonicalizedURL, "?")
  262. if len(parmas) > 1 {
  263. query = strings.Split(parmas[1], "&")
  264. for _, value := range query {
  265. if strings.HasPrefix(value, HEADER_STS_TOKEN_AMZ+"=") || strings.HasPrefix(value, HEADER_STS_TOKEN_OBS+"=") {
  266. if value[len(HEADER_STS_TOKEN_AMZ)+1:] != "" {
  267. securityToken = []string{value[len(HEADER_STS_TOKEN_AMZ)+1:]}
  268. isSecurityToken = true
  269. }
  270. }
  271. }
  272. }
  273. }
  274. logStringToSign := stringToSign
  275. if isSecurityToken && len(securityToken) > 0 {
  276. logStringToSign = strings.Replace(logStringToSign, securityToken[0], "******", -1)
  277. }
  278. doLog(LEVEL_DEBUG, "The v2 auth stringToSign:\n%s", logStringToSign)
  279. return stringToSign
  280. }
  281. func v2Auth(ak, sk, method, canonicalizedURL string, headers map[string][]string, isObs bool) map[string]string {
  282. stringToSign := getV2StringToSign(method, canonicalizedURL, headers, isObs)
  283. return map[string]string{"Signature": Base64Encode(HmacSha1([]byte(sk), []byte(stringToSign)))}
  284. }
  285. func getScope(region, shortDate string) string {
  286. return fmt.Sprintf("%s/%s/%s/%s", shortDate, region, V4_SERVICE_NAME, V4_SERVICE_SUFFIX)
  287. }
  288. func getCredential(ak, region, shortDate string) (string, string) {
  289. scope := getScope(region, shortDate)
  290. return fmt.Sprintf("%s/%s", ak, scope), scope
  291. }
  292. func getV4StringToSign(method, canonicalizedURL, queryURL, scope, longDate, payload string, signedHeaders []string, headers map[string][]string) string {
  293. canonicalRequest := make([]string, 0, 10+len(signedHeaders)*4)
  294. canonicalRequest = append(canonicalRequest, method)
  295. canonicalRequest = append(canonicalRequest, "\n")
  296. canonicalRequest = append(canonicalRequest, canonicalizedURL)
  297. canonicalRequest = append(canonicalRequest, "\n")
  298. canonicalRequest = append(canonicalRequest, queryURL)
  299. canonicalRequest = append(canonicalRequest, "\n")
  300. for _, signedHeader := range signedHeaders {
  301. values, _ := headers[signedHeader]
  302. for _, value := range values {
  303. canonicalRequest = append(canonicalRequest, signedHeader)
  304. canonicalRequest = append(canonicalRequest, ":")
  305. canonicalRequest = append(canonicalRequest, value)
  306. canonicalRequest = append(canonicalRequest, "\n")
  307. }
  308. }
  309. canonicalRequest = append(canonicalRequest, "\n")
  310. canonicalRequest = append(canonicalRequest, strings.Join(signedHeaders, ";"))
  311. canonicalRequest = append(canonicalRequest, "\n")
  312. canonicalRequest = append(canonicalRequest, payload)
  313. _canonicalRequest := strings.Join(canonicalRequest, "")
  314. var isSecurityToken bool
  315. var securityToken []string
  316. if securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_OBS]; !isSecurityToken {
  317. securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_AMZ]
  318. }
  319. var query []string
  320. if !isSecurityToken {
  321. query = strings.Split(queryURL, "&")
  322. for _, value := range query {
  323. if strings.HasPrefix(value, HEADER_STS_TOKEN_AMZ+"=") || strings.HasPrefix(value, HEADER_STS_TOKEN_OBS+"=") {
  324. if value[len(HEADER_STS_TOKEN_AMZ)+1:] != "" {
  325. securityToken = []string{value[len(HEADER_STS_TOKEN_AMZ)+1:]}
  326. isSecurityToken = true
  327. }
  328. }
  329. }
  330. }
  331. logCanonicalRequest := _canonicalRequest
  332. if isSecurityToken && len(securityToken) > 0 {
  333. logCanonicalRequest = strings.Replace(logCanonicalRequest, securityToken[0], "******", -1)
  334. }
  335. doLog(LEVEL_DEBUG, "The v4 auth canonicalRequest:\n%s", logCanonicalRequest)
  336. stringToSign := make([]string, 0, 7)
  337. stringToSign = append(stringToSign, V4_HASH_PREFIX)
  338. stringToSign = append(stringToSign, "\n")
  339. stringToSign = append(stringToSign, longDate)
  340. stringToSign = append(stringToSign, "\n")
  341. stringToSign = append(stringToSign, scope)
  342. stringToSign = append(stringToSign, "\n")
  343. stringToSign = append(stringToSign, HexSha256([]byte(_canonicalRequest)))
  344. _stringToSign := strings.Join(stringToSign, "")
  345. doLog(LEVEL_DEBUG, "The v4 auth stringToSign:\n%s", _stringToSign)
  346. return _stringToSign
  347. }
  348. func getSignedHeaders(headers map[string][]string) ([]string, map[string][]string) {
  349. length := len(headers)
  350. _headers := make(map[string][]string, length)
  351. signedHeaders := make([]string, 0, length)
  352. for key, value := range headers {
  353. _key := strings.ToLower(strings.TrimSpace(key))
  354. if _key != "" {
  355. signedHeaders = append(signedHeaders, _key)
  356. _headers[_key] = value
  357. } else {
  358. delete(headers, key)
  359. }
  360. }
  361. sort.Strings(signedHeaders)
  362. return signedHeaders, _headers
  363. }
  364. func getSignature(stringToSign, sk, region, shortDate string) string {
  365. key := HmacSha256([]byte(V4_HASH_PRE+sk), []byte(shortDate))
  366. key = HmacSha256(key, []byte(region))
  367. key = HmacSha256(key, []byte(V4_SERVICE_NAME))
  368. key = HmacSha256(key, []byte(V4_SERVICE_SUFFIX))
  369. return Hex(HmacSha256(key, []byte(stringToSign)))
  370. }
  371. // V4Auth is a wrapper for v4Auth
  372. func V4Auth(ak, sk, region, method, canonicalizedURL, queryURL string, headers map[string][]string) map[string]string {
  373. return v4Auth(ak, sk, region, method, canonicalizedURL, queryURL, headers)
  374. }
  375. func v4Auth(ak, sk, region, method, canonicalizedURL, queryURL string, headers map[string][]string) map[string]string {
  376. var t time.Time
  377. if val, ok := headers[HEADER_DATE_AMZ]; ok {
  378. var err error
  379. t, err = time.Parse(LONG_DATE_FORMAT, val[0])
  380. if err != nil {
  381. t = time.Now().UTC()
  382. }
  383. } else if val, ok := headers[PARAM_DATE_AMZ_CAMEL]; ok {
  384. var err error
  385. t, err = time.Parse(LONG_DATE_FORMAT, val[0])
  386. if err != nil {
  387. t = time.Now().UTC()
  388. }
  389. } else if val, ok := headers[HEADER_DATE_CAMEL]; ok {
  390. var err error
  391. t, err = time.Parse(RFC1123_FORMAT, val[0])
  392. if err != nil {
  393. t = time.Now().UTC()
  394. }
  395. } else if val, ok := headers[strings.ToLower(HEADER_DATE_CAMEL)]; ok {
  396. var err error
  397. t, err = time.Parse(RFC1123_FORMAT, val[0])
  398. if err != nil {
  399. t = time.Now().UTC()
  400. }
  401. } else {
  402. t = time.Now().UTC()
  403. }
  404. shortDate := t.Format(SHORT_DATE_FORMAT)
  405. longDate := t.Format(LONG_DATE_FORMAT)
  406. signedHeaders, _headers := getSignedHeaders(headers)
  407. credential, scope := getCredential(ak, region, shortDate)
  408. payload := UNSIGNED_PAYLOAD
  409. if val, ok := headers[HEADER_CONTENT_SHA256_AMZ]; ok {
  410. payload = val[0]
  411. }
  412. stringToSign := getV4StringToSign(method, canonicalizedURL, queryURL, scope, longDate, payload, signedHeaders, _headers)
  413. signature := getSignature(stringToSign, sk, region, shortDate)
  414. ret := make(map[string]string, 3)
  415. ret["Credential"] = credential
  416. ret["SignedHeaders"] = strings.Join(signedHeaders, ";")
  417. ret["Signature"] = signature
  418. return ret
  419. }