http_aws_sigv4.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) 1998 - 2021, Daniel Stenberg, <[email protected]>, et al.
  9. *
  10. * This software is licensed as described in the file COPYING, which
  11. * you should have received as part of this distribution. The terms
  12. * are also available at https://curl.haxx.se/docs/copyright.html.
  13. *
  14. * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  15. * copies of the Software, and permit persons to whom the Software is
  16. * furnished to do so, under the terms of the COPYING file.
  17. *
  18. * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  19. * KIND, either express or implied.
  20. *
  21. ***************************************************************************/
  22. #include "curl_setup.h"
  23. #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
  24. #include "urldata.h"
  25. #include "strcase.h"
  26. #include "strdup.h"
  27. #include "vauth/vauth.h"
  28. #include "vauth/digest.h"
  29. #include "http_aws_sigv4.h"
  30. #include "curl_sha256.h"
  31. #include "transfer.h"
  32. #include "strcase.h"
  33. #include "parsedate.h"
  34. #include "sendf.h"
  35. #include <time.h>
  36. /* The last 3 #include files should be in this order */
  37. #include "curl_printf.h"
  38. #include "curl_memory.h"
  39. #include "memdebug.h"
  40. #define HMAC_SHA256(k, kl, d, dl, o) \
  41. do { \
  42. ret = Curl_hmacit(Curl_HMAC_SHA256, \
  43. (unsigned char *)k, \
  44. (unsigned int)kl, \
  45. (unsigned char *)d, \
  46. (unsigned int)dl, o); \
  47. if(ret != CURLE_OK) { \
  48. goto fail; \
  49. } \
  50. } while(0)
  51. static void sha256_to_hex(char *dst, unsigned char *sha, size_t dst_l)
  52. {
  53. int i;
  54. DEBUGASSERT(dst_l >= 65);
  55. for(i = 0; i < 32; ++i) {
  56. curl_msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]);
  57. }
  58. }
  59. CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
  60. {
  61. CURLcode ret = CURLE_OUT_OF_MEMORY;
  62. struct connectdata *conn = data->conn;
  63. size_t len;
  64. const char *tmp0;
  65. const char *tmp1;
  66. char *provider0_low = NULL;
  67. char *provider0_up = NULL;
  68. char *provider1_low = NULL;
  69. char *provider1_mid = NULL;
  70. char *region = NULL;
  71. char *service = NULL;
  72. const char *hostname = conn->host.name;
  73. #ifdef DEBUGBUILD
  74. char *force_timestamp;
  75. #endif
  76. time_t clock;
  77. struct tm tm;
  78. char timestamp[17];
  79. char date[9];
  80. const char *content_type = Curl_checkheaders(data, "Content-Type");
  81. char *canonical_headers = NULL;
  82. char *signed_headers = NULL;
  83. Curl_HttpReq httpreq;
  84. const char *method;
  85. const char *post_data = data->set.postfields ? data->set.postfields : "";
  86. unsigned char sha_hash[32];
  87. char sha_hex[65];
  88. char *canonical_request = NULL;
  89. char *request_type = NULL;
  90. char *credential_scope = NULL;
  91. char *str_to_sign = NULL;
  92. const char *user = data->state.aptr.user ? data->state.aptr.user : "";
  93. const char *passwd = data->state.aptr.passwd ? data->state.aptr.passwd : "";
  94. char *secret = NULL;
  95. unsigned char tmp_sign0[32] = {0};
  96. unsigned char tmp_sign1[32] = {0};
  97. char *auth_headers = NULL;
  98. DEBUGASSERT(!proxy);
  99. (void)proxy;
  100. if(Curl_checkheaders(data, "Authorization")) {
  101. /* Authorization already present, Bailing out */
  102. return CURLE_OK;
  103. }
  104. /*
  105. * Parameters parsing
  106. * Google and Outscale use the same OSC or GOOG,
  107. * but Amazon uses AWS and AMZ for header arguments.
  108. * AWS is the default because most of non-amazon providers
  109. * are still using aws:amz as a prefix.
  110. */
  111. tmp0 = data->set.str[STRING_AWS_SIGV4] ?
  112. data->set.str[STRING_AWS_SIGV4] : "aws:amz";
  113. tmp1 = strchr(tmp0, ':');
  114. len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
  115. if(len < 1) {
  116. infof(data, "first provider can't be empty\n");
  117. ret = CURLE_BAD_FUNCTION_ARGUMENT;
  118. goto fail;
  119. }
  120. provider0_low = malloc(len + 1);
  121. provider0_up = malloc(len + 1);
  122. if(!provider0_low || !provider0_up) {
  123. goto fail;
  124. }
  125. Curl_strntolower(provider0_low, tmp0, len);
  126. provider0_low[len] = '\0';
  127. Curl_strntoupper(provider0_up, tmp0, len);
  128. provider0_up[len] = '\0';
  129. if(tmp1) {
  130. tmp0 = tmp1 + 1;
  131. tmp1 = strchr(tmp0, ':');
  132. len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
  133. if(len < 1) {
  134. infof(data, "second provider can't be empty\n");
  135. ret = CURLE_BAD_FUNCTION_ARGUMENT;
  136. goto fail;
  137. }
  138. provider1_low = malloc(len + 1);
  139. provider1_mid = malloc(len + 1);
  140. if(!provider1_low || !provider1_mid) {
  141. goto fail;
  142. }
  143. Curl_strntolower(provider1_low, tmp0, len);
  144. provider1_low[len] = '\0';
  145. Curl_strntolower(provider1_mid, tmp0, len);
  146. provider1_mid[0] = Curl_raw_toupper(provider1_mid[0]);
  147. provider1_mid[len] = '\0';
  148. if(tmp1) {
  149. tmp0 = tmp1 + 1;
  150. tmp1 = strchr(tmp0, ':');
  151. len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
  152. if(len < 1) {
  153. infof(data, "region can't be empty\n");
  154. ret = CURLE_BAD_FUNCTION_ARGUMENT;
  155. goto fail;
  156. }
  157. region = Curl_memdup(tmp0, len + 1);
  158. if(!region) {
  159. goto fail;
  160. }
  161. region[len] = '\0';
  162. if(tmp1) {
  163. tmp0 = tmp1 + 1;
  164. service = strdup(tmp0);
  165. if(!service) {
  166. goto fail;
  167. }
  168. if(strlen(service) < 1) {
  169. infof(data, "service can't be empty\n");
  170. ret = CURLE_BAD_FUNCTION_ARGUMENT;
  171. goto fail;
  172. }
  173. }
  174. }
  175. }
  176. else {
  177. provider1_low = Curl_memdup(provider0_low, len + 1);
  178. provider1_mid = Curl_memdup(provider0_low, len + 1);
  179. if(!provider1_low || !provider1_mid) {
  180. goto fail;
  181. }
  182. provider1_mid[0] = Curl_raw_toupper(provider1_mid[0]);
  183. }
  184. if(!service) {
  185. tmp0 = hostname;
  186. tmp1 = strchr(tmp0, '.');
  187. len = tmp1 - tmp0;
  188. if(!tmp1 || len < 1) {
  189. infof(data, "service missing in parameters or hostname\n");
  190. ret = CURLE_URL_MALFORMAT;
  191. goto fail;
  192. }
  193. service = Curl_memdup(tmp0, len + 1);
  194. if(!service) {
  195. goto fail;
  196. }
  197. service[len] = '\0';
  198. if(!region) {
  199. tmp0 = tmp1 + 1;
  200. tmp1 = strchr(tmp0, '.');
  201. len = tmp1 - tmp0;
  202. if(!tmp1 || len < 1) {
  203. infof(data, "region missing in parameters or hostname\n");
  204. ret = CURLE_URL_MALFORMAT;
  205. goto fail;
  206. }
  207. region = Curl_memdup(tmp0, len + 1);
  208. if(!region) {
  209. goto fail;
  210. }
  211. region[len] = '\0';
  212. }
  213. }
  214. #ifdef DEBUGBUILD
  215. force_timestamp = getenv("CURL_FORCETIME");
  216. if(force_timestamp)
  217. clock = 0;
  218. else
  219. time(&clock);
  220. #else
  221. time(&clock);
  222. #endif
  223. ret = Curl_gmtime(clock, &tm);
  224. if(ret != CURLE_OK) {
  225. goto fail;
  226. }
  227. if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
  228. goto fail;
  229. }
  230. memcpy(date, timestamp, sizeof(date));
  231. date[sizeof(date) - 1] = 0;
  232. if(content_type) {
  233. content_type = strchr(content_type, ':');
  234. if(!content_type) {
  235. ret = CURLE_FAILED_INIT;
  236. goto fail;
  237. }
  238. content_type++;
  239. /* Skip whitespace now */
  240. while(*content_type == ' ' || *content_type == '\t')
  241. ++content_type;
  242. canonical_headers = curl_maprintf("content-type:%s\n"
  243. "host:%s\n"
  244. "x-%s-date:%s\n",
  245. content_type,
  246. hostname,
  247. provider1_low, timestamp);
  248. signed_headers = curl_maprintf("content-type;host;x-%s-date",
  249. provider1_low);
  250. }
  251. else {
  252. canonical_headers = curl_maprintf("host:%s\n"
  253. "x-%s-date:%s\n",
  254. hostname,
  255. provider1_low, timestamp);
  256. signed_headers = curl_maprintf("host;x-%s-date", provider1_low);
  257. }
  258. if(!canonical_headers || !signed_headers) {
  259. goto fail;
  260. }
  261. Curl_sha256it(sha_hash,
  262. (const unsigned char *) post_data, strlen(post_data));
  263. sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
  264. Curl_http_method(data, conn, &method, &httpreq);
  265. canonical_request =
  266. curl_maprintf("%s\n" /* HTTPRequestMethod */
  267. "%s\n" /* CanonicalURI */
  268. "%s\n" /* CanonicalQueryString */
  269. "%s\n" /* CanonicalHeaders */
  270. "%s\n" /* SignedHeaders */
  271. "%s", /* HashedRequestPayload in hex */
  272. method,
  273. data->state.up.path,
  274. data->state.up.query ? data->state.up.query : "",
  275. canonical_headers,
  276. signed_headers,
  277. sha_hex);
  278. if(!canonical_request) {
  279. goto fail;
  280. }
  281. request_type = curl_maprintf("%s4_request", provider0_low);
  282. if(!request_type) {
  283. goto fail;
  284. }
  285. credential_scope = curl_maprintf("%s/%s/%s/%s",
  286. date, region, service, request_type);
  287. if(!credential_scope) {
  288. goto fail;
  289. }
  290. Curl_sha256it(sha_hash, (unsigned char *) canonical_request,
  291. strlen(canonical_request));
  292. sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
  293. /*
  294. * Google allow to use rsa key instead of HMAC, so this code might change
  295. * In the furure, but for now we support only HMAC version
  296. */
  297. str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */
  298. "%s\n" /* RequestDateTime */
  299. "%s\n" /* CredentialScope */
  300. "%s", /* HashedCanonicalRequest in hex */
  301. provider0_up,
  302. timestamp,
  303. credential_scope,
  304. sha_hex);
  305. if(!str_to_sign) {
  306. goto fail;
  307. }
  308. secret = curl_maprintf("%s4%s", provider0_up, passwd);
  309. if(!secret) {
  310. goto fail;
  311. }
  312. HMAC_SHA256(secret, strlen(secret),
  313. date, strlen(date), tmp_sign0);
  314. HMAC_SHA256(tmp_sign0, sizeof(tmp_sign0),
  315. region, strlen(region), tmp_sign1);
  316. HMAC_SHA256(tmp_sign1, sizeof(tmp_sign1),
  317. service, strlen(service), tmp_sign0);
  318. HMAC_SHA256(tmp_sign0, sizeof(tmp_sign0),
  319. request_type, strlen(request_type), tmp_sign1);
  320. HMAC_SHA256(tmp_sign1, sizeof(tmp_sign1),
  321. str_to_sign, strlen(str_to_sign), tmp_sign0);
  322. sha256_to_hex(sha_hex, tmp_sign0, sizeof(sha_hex));
  323. auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 "
  324. "Credential=%s/%s, "
  325. "SignedHeaders=%s, "
  326. "Signature=%s\r\n"
  327. "X-%s-Date: %s\r\n",
  328. provider0_up,
  329. user,
  330. credential_scope,
  331. signed_headers,
  332. sha_hex,
  333. provider1_mid,
  334. timestamp);
  335. if(!auth_headers) {
  336. goto fail;
  337. }
  338. Curl_safefree(data->state.aptr.userpwd);
  339. data->state.aptr.userpwd = auth_headers;
  340. data->state.authhost.done = TRUE;
  341. ret = CURLE_OK;
  342. fail:
  343. free(provider0_low);
  344. free(provider0_up);
  345. free(provider1_low);
  346. free(provider1_mid);
  347. free(region);
  348. free(service);
  349. free(canonical_headers);
  350. free(signed_headers);
  351. free(canonical_request);
  352. free(request_type);
  353. free(credential_scope);
  354. free(str_to_sign);
  355. free(secret);
  356. return ret;
  357. }
  358. #endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */