http_aws_sigv4.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, 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, STRCONST("Content-Type"));
  81. char *canonical_headers = NULL;
  82. char *signed_headers = NULL;
  83. Curl_HttpReq httpreq;
  84. const char *method;
  85. size_t post_data_len;
  86. const char *post_data = data->set.postfields ? data->set.postfields : "";
  87. unsigned char sha_hash[32];
  88. char sha_hex[65];
  89. char *canonical_request = NULL;
  90. char *request_type = NULL;
  91. char *credential_scope = NULL;
  92. char *str_to_sign = NULL;
  93. const char *user = data->state.aptr.user ? data->state.aptr.user : "";
  94. const char *passwd = data->state.aptr.passwd ? data->state.aptr.passwd : "";
  95. char *secret = NULL;
  96. unsigned char tmp_sign0[32] = {0};
  97. unsigned char tmp_sign1[32] = {0};
  98. char *auth_headers = NULL;
  99. DEBUGASSERT(!proxy);
  100. (void)proxy;
  101. if(Curl_checkheaders(data, STRCONST("Authorization"))) {
  102. /* Authorization already present, Bailing out */
  103. return CURLE_OK;
  104. }
  105. /*
  106. * Parameters parsing
  107. * Google and Outscale use the same OSC or GOOG,
  108. * but Amazon uses AWS and AMZ for header arguments.
  109. * AWS is the default because most of non-amazon providers
  110. * are still using aws:amz as a prefix.
  111. */
  112. tmp0 = data->set.str[STRING_AWS_SIGV4] ?
  113. data->set.str[STRING_AWS_SIGV4] : "aws:amz";
  114. tmp1 = strchr(tmp0, ':');
  115. len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
  116. if(len < 1) {
  117. infof(data, "first provider can't be empty");
  118. ret = CURLE_BAD_FUNCTION_ARGUMENT;
  119. goto fail;
  120. }
  121. provider0_low = malloc(len + 1);
  122. provider0_up = malloc(len + 1);
  123. if(!provider0_low || !provider0_up) {
  124. goto fail;
  125. }
  126. Curl_strntolower(provider0_low, tmp0, len);
  127. provider0_low[len] = '\0';
  128. Curl_strntoupper(provider0_up, tmp0, len);
  129. provider0_up[len] = '\0';
  130. if(tmp1) {
  131. tmp0 = tmp1 + 1;
  132. tmp1 = strchr(tmp0, ':');
  133. len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
  134. if(len < 1) {
  135. infof(data, "second provider can't be empty");
  136. ret = CURLE_BAD_FUNCTION_ARGUMENT;
  137. goto fail;
  138. }
  139. provider1_low = malloc(len + 1);
  140. provider1_mid = malloc(len + 1);
  141. if(!provider1_low || !provider1_mid) {
  142. goto fail;
  143. }
  144. Curl_strntolower(provider1_low, tmp0, len);
  145. provider1_low[len] = '\0';
  146. Curl_strntolower(provider1_mid, tmp0, len);
  147. provider1_mid[0] = Curl_raw_toupper(provider1_mid[0]);
  148. provider1_mid[len] = '\0';
  149. if(tmp1) {
  150. tmp0 = tmp1 + 1;
  151. tmp1 = strchr(tmp0, ':');
  152. len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
  153. if(len < 1) {
  154. infof(data, "region can't be empty");
  155. ret = CURLE_BAD_FUNCTION_ARGUMENT;
  156. goto fail;
  157. }
  158. region = Curl_memdup(tmp0, len + 1);
  159. if(!region) {
  160. goto fail;
  161. }
  162. region[len] = '\0';
  163. if(tmp1) {
  164. tmp0 = tmp1 + 1;
  165. service = strdup(tmp0);
  166. if(!service) {
  167. goto fail;
  168. }
  169. if(strlen(service) < 1) {
  170. infof(data, "service can't be empty");
  171. ret = CURLE_BAD_FUNCTION_ARGUMENT;
  172. goto fail;
  173. }
  174. }
  175. }
  176. }
  177. else {
  178. provider1_low = Curl_memdup(provider0_low, len + 1);
  179. provider1_mid = Curl_memdup(provider0_low, len + 1);
  180. if(!provider1_low || !provider1_mid) {
  181. goto fail;
  182. }
  183. provider1_mid[0] = Curl_raw_toupper(provider1_mid[0]);
  184. }
  185. if(!service) {
  186. tmp0 = hostname;
  187. tmp1 = strchr(tmp0, '.');
  188. if(!tmp1) {
  189. infof(data, "service missing in parameters or hostname");
  190. ret = CURLE_URL_MALFORMAT;
  191. goto fail;
  192. }
  193. len = tmp1 - tmp0;
  194. service = Curl_memdup(tmp0, len + 1);
  195. if(!service) {
  196. goto fail;
  197. }
  198. service[len] = '\0';
  199. if(!region) {
  200. tmp0 = tmp1 + 1;
  201. tmp1 = strchr(tmp0, '.');
  202. if(!tmp1) {
  203. infof(data, "region missing in parameters or hostname");
  204. ret = CURLE_URL_MALFORMAT;
  205. goto fail;
  206. }
  207. len = tmp1 - tmp0;
  208. region = Curl_memdup(tmp0, len + 1);
  209. if(!region) {
  210. goto fail;
  211. }
  212. region[len] = '\0';
  213. }
  214. }
  215. #ifdef DEBUGBUILD
  216. force_timestamp = getenv("CURL_FORCETIME");
  217. if(force_timestamp)
  218. clock = 0;
  219. else
  220. time(&clock);
  221. #else
  222. time(&clock);
  223. #endif
  224. ret = Curl_gmtime(clock, &tm);
  225. if(ret != CURLE_OK) {
  226. goto fail;
  227. }
  228. if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
  229. goto fail;
  230. }
  231. memcpy(date, timestamp, sizeof(date));
  232. date[sizeof(date) - 1] = 0;
  233. if(content_type) {
  234. content_type = strchr(content_type, ':');
  235. if(!content_type) {
  236. ret = CURLE_FAILED_INIT;
  237. goto fail;
  238. }
  239. content_type++;
  240. /* Skip whitespace now */
  241. while(*content_type == ' ' || *content_type == '\t')
  242. ++content_type;
  243. canonical_headers = curl_maprintf("content-type:%s\n"
  244. "host:%s\n"
  245. "x-%s-date:%s\n",
  246. content_type,
  247. hostname,
  248. provider1_low, timestamp);
  249. signed_headers = curl_maprintf("content-type;host;x-%s-date",
  250. provider1_low);
  251. }
  252. else {
  253. canonical_headers = curl_maprintf("host:%s\n"
  254. "x-%s-date:%s\n",
  255. hostname,
  256. provider1_low, timestamp);
  257. signed_headers = curl_maprintf("host;x-%s-date", provider1_low);
  258. }
  259. if(!canonical_headers || !signed_headers) {
  260. goto fail;
  261. }
  262. if(data->set.postfieldsize < 0)
  263. post_data_len = strlen(post_data);
  264. else
  265. post_data_len = (size_t)data->set.postfieldsize;
  266. if(Curl_sha256it(sha_hash, (const unsigned char *) post_data,
  267. post_data_len)) {
  268. goto fail;
  269. }
  270. sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
  271. Curl_http_method(data, conn, &method, &httpreq);
  272. canonical_request =
  273. curl_maprintf("%s\n" /* HTTPRequestMethod */
  274. "%s\n" /* CanonicalURI */
  275. "%s\n" /* CanonicalQueryString */
  276. "%s\n" /* CanonicalHeaders */
  277. "%s\n" /* SignedHeaders */
  278. "%s", /* HashedRequestPayload in hex */
  279. method,
  280. data->state.up.path,
  281. data->state.up.query ? data->state.up.query : "",
  282. canonical_headers,
  283. signed_headers,
  284. sha_hex);
  285. if(!canonical_request) {
  286. goto fail;
  287. }
  288. request_type = curl_maprintf("%s4_request", provider0_low);
  289. if(!request_type) {
  290. goto fail;
  291. }
  292. credential_scope = curl_maprintf("%s/%s/%s/%s",
  293. date, region, service, request_type);
  294. if(!credential_scope) {
  295. goto fail;
  296. }
  297. if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request,
  298. strlen(canonical_request))) {
  299. goto fail;
  300. }
  301. sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
  302. /*
  303. * Google allow to use rsa key instead of HMAC, so this code might change
  304. * In the future, but for now we support only HMAC version
  305. */
  306. str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */
  307. "%s\n" /* RequestDateTime */
  308. "%s\n" /* CredentialScope */
  309. "%s", /* HashedCanonicalRequest in hex */
  310. provider0_up,
  311. timestamp,
  312. credential_scope,
  313. sha_hex);
  314. if(!str_to_sign) {
  315. goto fail;
  316. }
  317. secret = curl_maprintf("%s4%s", provider0_up, passwd);
  318. if(!secret) {
  319. goto fail;
  320. }
  321. HMAC_SHA256(secret, strlen(secret),
  322. date, strlen(date), tmp_sign0);
  323. HMAC_SHA256(tmp_sign0, sizeof(tmp_sign0),
  324. region, strlen(region), tmp_sign1);
  325. HMAC_SHA256(tmp_sign1, sizeof(tmp_sign1),
  326. service, strlen(service), tmp_sign0);
  327. HMAC_SHA256(tmp_sign0, sizeof(tmp_sign0),
  328. request_type, strlen(request_type), tmp_sign1);
  329. HMAC_SHA256(tmp_sign1, sizeof(tmp_sign1),
  330. str_to_sign, strlen(str_to_sign), tmp_sign0);
  331. sha256_to_hex(sha_hex, tmp_sign0, sizeof(sha_hex));
  332. auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 "
  333. "Credential=%s/%s, "
  334. "SignedHeaders=%s, "
  335. "Signature=%s\r\n"
  336. "X-%s-Date: %s\r\n",
  337. provider0_up,
  338. user,
  339. credential_scope,
  340. signed_headers,
  341. sha_hex,
  342. provider1_mid,
  343. timestamp);
  344. if(!auth_headers) {
  345. goto fail;
  346. }
  347. Curl_safefree(data->state.aptr.userpwd);
  348. data->state.aptr.userpwd = auth_headers;
  349. data->state.authhost.done = TRUE;
  350. ret = CURLE_OK;
  351. fail:
  352. free(provider0_low);
  353. free(provider0_up);
  354. free(provider1_low);
  355. free(provider1_mid);
  356. free(region);
  357. free(service);
  358. free(canonical_headers);
  359. free(signed_headers);
  360. free(canonical_request);
  361. free(request_type);
  362. free(credential_scope);
  363. free(str_to_sign);
  364. free(secret);
  365. return ret;
  366. }
  367. #endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */