1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 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 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS)
28
29 #include "urldata.h"
30 #include "strcase.h"
31 #include "strdup.h"
32 #include "http_aws_sigv4.h"
33 #include "curl_sha256.h"
34 #include "transfer.h"
35 #include "parsedate.h"
36 #include "sendf.h"
37 #include "escape.h"
38 #include "strparse.h"
39
40 #include <time.h>
41
42 /* The last 3 #include files should be in this order */
43 #include "curl_printf.h"
44 #include "curl_memory.h"
45 #include "memdebug.h"
46
47 #include "slist.h"
48
49 #define HMAC_SHA256(k, kl, d, dl, o) \
50 do { \
51 result = Curl_hmacit(&Curl_HMAC_SHA256, \
52 (unsigned char *)k, \
53 kl, \
54 (unsigned char *)d, \
55 dl, o); \
56 if(result) { \
57 goto fail; \
58 } \
59 } while(0)
60
61 #define TIMESTAMP_SIZE 17
62
63 /* hex-encoded with trailing null */
64 #define SHA256_HEX_LENGTH (2 * CURL_SHA256_DIGEST_LENGTH + 1)
65
sha256_to_hex(char * dst,unsigned char * sha)66 static void sha256_to_hex(char *dst, unsigned char *sha)
67 {
68 Curl_hexencode(sha, CURL_SHA256_DIGEST_LENGTH,
69 (unsigned char *)dst, SHA256_HEX_LENGTH);
70 }
71
find_date_hdr(struct Curl_easy * data,const char * sig_hdr)72 static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr)
73 {
74 char *tmp = Curl_checkheaders(data, sig_hdr, strlen(sig_hdr));
75
76 if(tmp)
77 return tmp;
78 return Curl_checkheaders(data, STRCONST("Date"));
79 }
80
81 /* remove whitespace, and lowercase all headers */
trim_headers(struct curl_slist * head)82 static void trim_headers(struct curl_slist *head)
83 {
84 struct curl_slist *l;
85 for(l = head; l; l = l->next) {
86 char *value; /* to read from */
87 char *store;
88 size_t colon = strcspn(l->data, ":");
89 Curl_strntolower(l->data, l->data, colon);
90
91 value = &l->data[colon];
92 if(!*value)
93 continue;
94 ++value;
95 store = value;
96
97 /* skip leading whitespace */
98 while(*value && ISBLANK(*value))
99 value++;
100
101 while(*value) {
102 int space = 0;
103 while(*value && ISBLANK(*value)) {
104 value++;
105 space++;
106 }
107 if(space) {
108 /* replace any number of consecutive whitespace with a single space,
109 unless at the end of the string, then nothing */
110 if(*value)
111 *store++ = ' ';
112 }
113 else
114 *store++ = *value++;
115 }
116 *store = 0; /* null terminate */
117 }
118 }
119
120 /* maximum length for the aws sivg4 parts */
121 #define MAX_SIGV4_LEN 64
122 #define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date"))
123
124 /* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */
125 #define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1)
126
127 /* alphabetically compare two headers by their name, expecting
128 headers to use ':' at this point */
compare_header_names(const char * a,const char * b)129 static int compare_header_names(const char *a, const char *b)
130 {
131 const char *colon_a;
132 const char *colon_b;
133 size_t len_a;
134 size_t len_b;
135 size_t min_len;
136 int cmp;
137
138 colon_a = strchr(a, ':');
139 colon_b = strchr(b, ':');
140
141 DEBUGASSERT(colon_a);
142 DEBUGASSERT(colon_b);
143
144 len_a = colon_a ? (size_t)(colon_a - a) : strlen(a);
145 len_b = colon_b ? (size_t)(colon_b - b) : strlen(b);
146
147 min_len = (len_a < len_b) ? len_a : len_b;
148
149 cmp = strncmp(a, b, min_len);
150
151 /* return the shorter of the two if one is shorter */
152 if(!cmp)
153 return (int)(len_a - len_b);
154
155 return cmp;
156 }
157
158 /* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */
make_headers(struct Curl_easy * data,const char * hostname,char * timestamp,const char * provider1,size_t plen,char ** date_header,char * content_sha256_header,struct dynbuf * canonical_headers,struct dynbuf * signed_headers)159 static CURLcode make_headers(struct Curl_easy *data,
160 const char *hostname,
161 char *timestamp,
162 const char *provider1,
163 size_t plen, /* length of provider1 */
164 char **date_header,
165 char *content_sha256_header,
166 struct dynbuf *canonical_headers,
167 struct dynbuf *signed_headers)
168 {
169 char date_hdr_key[DATE_HDR_KEY_LEN];
170 char date_full_hdr[DATE_FULL_HDR_LEN];
171 struct curl_slist *head = NULL;
172 struct curl_slist *tmp_head = NULL;
173 CURLcode ret = CURLE_OUT_OF_MEMORY;
174 struct curl_slist *l;
175 bool again = TRUE;
176
177 msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%.*s-Date",
178 (int)plen, provider1);
179 /* provider1 ucfirst */
180 Curl_strntolower(&date_hdr_key[2], provider1, plen);
181 date_hdr_key[2] = Curl_raw_toupper(provider1[0]);
182
183 msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
184 "x-%.*s-date:%s", (int)plen, provider1, timestamp);
185 /* provider1 lowercase */
186 Curl_strntolower(&date_full_hdr[2], provider1, plen);
187
188 if(!Curl_checkheaders(data, STRCONST("Host"))) {
189 char *fullhost;
190
191 if(data->state.aptr.host) {
192 /* remove /r/n as the separator for canonical request must be '\n' */
193 size_t pos = strcspn(data->state.aptr.host, "\n\r");
194 fullhost = Curl_memdup0(data->state.aptr.host, pos);
195 }
196 else
197 fullhost = aprintf("host:%s", hostname);
198
199 if(fullhost)
200 head = Curl_slist_append_nodup(NULL, fullhost);
201 if(!head) {
202 free(fullhost);
203 goto fail;
204 }
205 }
206
207
208 if(*content_sha256_header) {
209 tmp_head = curl_slist_append(head, content_sha256_header);
210 if(!tmp_head)
211 goto fail;
212 head = tmp_head;
213 }
214
215 /* copy user headers to our header list. the logic is based on how http.c
216 handles user headers.
217
218 user headers in format 'name:' with no value are used to signal that an
219 internal header of that name should be removed. those user headers are not
220 added to this list.
221
222 user headers in format 'name;' with no value are used to signal that a
223 header of that name with no value should be sent. those user headers are
224 added to this list but in the format that they will be sent, ie the
225 semi-colon is changed to a colon for format 'name:'.
226
227 user headers with a value of whitespace only, or without a colon or
228 semi-colon, are not added to this list.
229 */
230 for(l = data->set.headers; l; l = l->next) {
231 char *dupdata, *ptr;
232 char *sep = strchr(l->data, ':');
233 if(!sep)
234 sep = strchr(l->data, ';');
235 if(!sep || (*sep == ':' && !*(sep + 1)))
236 continue;
237 for(ptr = sep + 1; ISSPACE(*ptr); ++ptr)
238 ;
239 if(!*ptr && ptr != sep + 1) /* a value of whitespace only */
240 continue;
241 dupdata = strdup(l->data);
242 if(!dupdata)
243 goto fail;
244 dupdata[sep - l->data] = ':';
245 tmp_head = Curl_slist_append_nodup(head, dupdata);
246 if(!tmp_head) {
247 free(dupdata);
248 goto fail;
249 }
250 head = tmp_head;
251 }
252
253 trim_headers(head);
254
255 *date_header = find_date_hdr(data, date_hdr_key);
256 if(!*date_header) {
257 tmp_head = curl_slist_append(head, date_full_hdr);
258 if(!tmp_head)
259 goto fail;
260 head = tmp_head;
261 *date_header = aprintf("%s: %s\r\n", date_hdr_key, timestamp);
262 }
263 else {
264 char *value;
265 char *endp;
266 value = strchr(*date_header, ':');
267 if(!value) {
268 *date_header = NULL;
269 goto fail;
270 }
271 ++value;
272 while(ISBLANK(*value))
273 ++value;
274 endp = value;
275 while(*endp && ISALNUM(*endp))
276 ++endp;
277 /* 16 bytes => "19700101T000000Z" */
278 if((endp - value) == TIMESTAMP_SIZE - 1) {
279 memcpy(timestamp, value, TIMESTAMP_SIZE - 1);
280 timestamp[TIMESTAMP_SIZE - 1] = 0;
281 }
282 else
283 /* bad timestamp length */
284 timestamp[0] = 0;
285 *date_header = NULL;
286 }
287
288 /* alpha-sort by header name in a case sensitive manner */
289 do {
290 again = FALSE;
291 for(l = head; l; l = l->next) {
292 struct curl_slist *next = l->next;
293
294 if(next && compare_header_names(l->data, next->data) > 0) {
295 char *tmp = l->data;
296
297 l->data = next->data;
298 next->data = tmp;
299 again = TRUE;
300 }
301 }
302 } while(again);
303
304 for(l = head; l; l = l->next) {
305 char *tmp;
306
307 if(Curl_dyn_add(canonical_headers, l->data))
308 goto fail;
309 if(Curl_dyn_add(canonical_headers, "\n"))
310 goto fail;
311
312 tmp = strchr(l->data, ':');
313 if(tmp)
314 *tmp = 0;
315
316 if(l != head) {
317 if(Curl_dyn_add(signed_headers, ";"))
318 goto fail;
319 }
320 if(Curl_dyn_add(signed_headers, l->data))
321 goto fail;
322 }
323
324 ret = CURLE_OK;
325 fail:
326 curl_slist_free_all(head);
327
328 return ret;
329 }
330
331 #define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256"))
332 /* add 2 for ": " between header name and value */
333 #define CONTENT_SHA256_HDR_LEN (CONTENT_SHA256_KEY_LEN + 2 + \
334 SHA256_HEX_LENGTH)
335
336 /* try to parse a payload hash from the content-sha256 header */
parse_content_sha_hdr(struct Curl_easy * data,const char * provider1,size_t plen,size_t * value_len)337 static char *parse_content_sha_hdr(struct Curl_easy *data,
338 const char *provider1,
339 size_t plen,
340 size_t *value_len)
341 {
342 char key[CONTENT_SHA256_KEY_LEN];
343 size_t key_len;
344 char *value;
345 size_t len;
346
347 key_len = msnprintf(key, sizeof(key), "x-%.*s-content-sha256",
348 (int)plen, provider1);
349
350 value = Curl_checkheaders(data, key, key_len);
351 if(!value)
352 return NULL;
353
354 value = strchr(value, ':');
355 if(!value)
356 return NULL;
357 ++value;
358
359 while(*value && ISBLANK(*value))
360 ++value;
361
362 len = strlen(value);
363 while(len > 0 && ISBLANK(value[len-1]))
364 --len;
365
366 *value_len = len;
367 return value;
368 }
369
calc_payload_hash(struct Curl_easy * data,unsigned char * sha_hash,char * sha_hex)370 static CURLcode calc_payload_hash(struct Curl_easy *data,
371 unsigned char *sha_hash, char *sha_hex)
372 {
373 const char *post_data = data->set.postfields;
374 size_t post_data_len = 0;
375 CURLcode result;
376
377 if(post_data) {
378 if(data->set.postfieldsize < 0)
379 post_data_len = strlen(post_data);
380 else
381 post_data_len = (size_t)data->set.postfieldsize;
382 }
383 result = Curl_sha256it(sha_hash, (const unsigned char *) post_data,
384 post_data_len);
385 if(!result)
386 sha256_to_hex(sha_hex, sha_hash);
387 return result;
388 }
389
390 #define S3_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD"
391
calc_s3_payload_hash(struct Curl_easy * data,Curl_HttpReq httpreq,char * provider1,size_t plen,unsigned char * sha_hash,char * sha_hex,char * header)392 static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
393 Curl_HttpReq httpreq, char *provider1,
394 size_t plen,
395 unsigned char *sha_hash,
396 char *sha_hex, char *header)
397 {
398 bool empty_method = (httpreq == HTTPREQ_GET || httpreq == HTTPREQ_HEAD);
399 /* The request method or filesize indicate no request payload */
400 bool empty_payload = (empty_method || data->set.filesize == 0);
401 /* The POST payload is in memory */
402 bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields);
403 CURLcode ret = CURLE_OUT_OF_MEMORY;
404
405 if(empty_payload || post_payload) {
406 /* Calculate a real hash when we know the request payload */
407 ret = calc_payload_hash(data, sha_hash, sha_hex);
408 if(ret)
409 goto fail;
410 }
411 else {
412 /* Fall back to s3's UNSIGNED-PAYLOAD */
413 size_t len = sizeof(S3_UNSIGNED_PAYLOAD) - 1;
414 DEBUGASSERT(len < SHA256_HEX_LENGTH); /* 16 < 65 */
415 memcpy(sha_hex, S3_UNSIGNED_PAYLOAD, len);
416 sha_hex[len] = 0;
417 }
418
419 /* format the required content-sha256 header */
420 msnprintf(header, CONTENT_SHA256_HDR_LEN,
421 "x-%.*s-content-sha256: %s", (int)plen, provider1, sha_hex);
422
423 ret = CURLE_OK;
424 fail:
425 return ret;
426 }
427
428 struct pair {
429 const char *p;
430 size_t len;
431 };
432
compare_func(const void * a,const void * b)433 static int compare_func(const void *a, const void *b)
434 {
435 const struct pair *aa = a;
436 const struct pair *bb = b;
437 /* If one element is empty, the other is always sorted higher */
438 if(aa->len == 0 && bb->len == 0)
439 return 0;
440 if(aa->len == 0)
441 return -1;
442 if(bb->len == 0)
443 return 1;
444 return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len);
445 }
446
447 #define MAX_QUERYPAIRS 64
448
449 /**
450 * found_equals have a double meaning,
451 * detect if an equal have been found when called from canon_query,
452 * and mark that this function is called to compute the path,
453 * if found_equals is NULL.
454 */
canon_string(const char * q,size_t len,struct dynbuf * dq,bool * found_equals)455 static CURLcode canon_string(const char *q, size_t len,
456 struct dynbuf *dq, bool *found_equals)
457 {
458 CURLcode result = CURLE_OK;
459
460 for(; len && !result; q++, len--) {
461 if(ISALNUM(*q))
462 result = Curl_dyn_addn(dq, q, 1);
463 else {
464 switch(*q) {
465 case '-':
466 case '.':
467 case '_':
468 case '~':
469 /* allowed as-is */
470 result = Curl_dyn_addn(dq, q, 1);
471 break;
472 case '%':
473 /* uppercase the following if hexadecimal */
474 if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) {
475 char tmp[3]="%";
476 tmp[1] = Curl_raw_toupper(q[1]);
477 tmp[2] = Curl_raw_toupper(q[2]);
478 result = Curl_dyn_addn(dq, tmp, 3);
479 q += 2;
480 len -= 2;
481 }
482 else
483 /* '%' without a following two-digit hex, encode it */
484 result = Curl_dyn_addn(dq, "%25", 3);
485 break;
486 default: {
487 const char hex[] = "0123456789ABCDEF";
488 char out[3]={'%'};
489
490 if(!found_equals) {
491 /* if found_equals is NULL assuming, been in path */
492 if(*q == '/') {
493 /* allowed as if */
494 result = Curl_dyn_addn(dq, q, 1);
495 break;
496 }
497 }
498 else {
499 /* allowed as-is */
500 if(*q == '=') {
501 result = Curl_dyn_addn(dq, q, 1);
502 *found_equals = TRUE;
503 break;
504 }
505 }
506 /* URL encode */
507 out[1] = hex[((unsigned char)*q) >> 4];
508 out[2] = hex[*q & 0xf];
509 result = Curl_dyn_addn(dq, out, 3);
510 break;
511 }
512 }
513 }
514 }
515 return result;
516 }
517
518
canon_query(struct Curl_easy * data,const char * query,struct dynbuf * dq)519 static CURLcode canon_query(struct Curl_easy *data,
520 const char *query, struct dynbuf *dq)
521 {
522 CURLcode result = CURLE_OK;
523 int entry = 0;
524 int i;
525 const char *p = query;
526 struct pair array[MAX_QUERYPAIRS];
527 struct pair *ap = &array[0];
528 if(!query)
529 return result;
530
531 /* sort the name=value pairs first */
532 do {
533 char *amp;
534 entry++;
535 ap->p = p;
536 amp = strchr(p, '&');
537 if(amp)
538 ap->len = amp - p; /* excluding the ampersand */
539 else {
540 ap->len = strlen(p);
541 break;
542 }
543 ap++;
544 p = amp + 1;
545 } while(entry < MAX_QUERYPAIRS);
546 if(entry == MAX_QUERYPAIRS) {
547 /* too many query pairs for us */
548 failf(data, "aws-sigv4: too many query pairs in URL");
549 return CURLE_URL_MALFORMAT;
550 }
551
552 qsort(&array[0], entry, sizeof(struct pair), compare_func);
553
554 ap = &array[0];
555 for(i = 0; !result && (i < entry); i++, ap++) {
556 const char *q = ap->p;
557 bool found_equals = FALSE;
558 if(!ap->len)
559 continue;
560 result = canon_string(q, ap->len, dq, &found_equals);
561 if(!result && !found_equals) {
562 /* queries without value still need an equals */
563 result = Curl_dyn_addn(dq, "=", 1);
564 }
565 if(!result && i < entry - 1) {
566 /* insert ampersands between query pairs */
567 result = Curl_dyn_addn(dq, "&", 1);
568 }
569 }
570 return result;
571 }
572
573
Curl_output_aws_sigv4(struct Curl_easy * data,bool proxy)574 CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
575 {
576 CURLcode result = CURLE_OUT_OF_MEMORY;
577 struct connectdata *conn = data->conn;
578 size_t len;
579 char *line;
580 struct Curl_str provider0;
581 struct Curl_str provider1;
582 struct Curl_str region = { NULL, 0};
583 struct Curl_str service = { NULL, 0};
584 const char *hostname = conn->host.name;
585 time_t clock;
586 struct tm tm;
587 char timestamp[TIMESTAMP_SIZE];
588 char date[9];
589 struct dynbuf canonical_headers;
590 struct dynbuf signed_headers;
591 struct dynbuf canonical_query;
592 struct dynbuf canonical_path;
593 char *date_header = NULL;
594 Curl_HttpReq httpreq;
595 const char *method = NULL;
596 char *payload_hash = NULL;
597 size_t payload_hash_len = 0;
598 unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH];
599 char sha_hex[SHA256_HEX_LENGTH];
600 char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */
601 char *canonical_request = NULL;
602 char *request_type = NULL;
603 char *credential_scope = NULL;
604 char *str_to_sign = NULL;
605 const char *user = data->state.aptr.user ? data->state.aptr.user : "";
606 char *secret = NULL;
607 unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = {0};
608 unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = {0};
609 char *auth_headers = NULL;
610
611 DEBUGASSERT(!proxy);
612 (void)proxy;
613
614 if(Curl_checkheaders(data, STRCONST("Authorization"))) {
615 /* Authorization already present, Bailing out */
616 return CURLE_OK;
617 }
618
619 /* we init those buffers here, so goto fail will free initialized dynbuf */
620 Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
621 Curl_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
622 Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
623 Curl_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER);
624
625 /*
626 * Parameters parsing
627 * Google and Outscale use the same OSC or GOOG,
628 * but Amazon uses AWS and AMZ for header arguments.
629 * AWS is the default because most of non-amazon providers
630 * are still using aws:amz as a prefix.
631 */
632 line = data->set.str[STRING_AWS_SIGV4] ?
633 data->set.str[STRING_AWS_SIGV4] : (char *)"aws:amz";
634
635 /* provider0[:provider1[:region[:service]]]
636
637 No string can be longer than N bytes of non-whitespace
638 */
639 if(Curl_str_until(&line, &provider0, MAX_SIGV4_LEN, ':')) {
640 failf(data, "first aws-sigv4 provider cannot be empty");
641 result = CURLE_BAD_FUNCTION_ARGUMENT;
642 goto fail;
643 }
644 if(Curl_str_single(&line, ':') ||
645 Curl_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) {
646 provider1.str = provider0.str;
647 provider1.len = provider0.len;
648 }
649 else if(Curl_str_single(&line, ':') ||
650 Curl_str_until(&line, ®ion, MAX_SIGV4_LEN, ':') ||
651 Curl_str_single(&line, ':') ||
652 Curl_str_until(&line, &service, MAX_SIGV4_LEN, ':')) {
653 /* nothing to do */
654 }
655
656 if(!service.len) {
657 char *hostdot = strchr(hostname, '.');
658 if(!hostdot) {
659 failf(data, "aws-sigv4: service missing in parameters and hostname");
660 result = CURLE_URL_MALFORMAT;
661 goto fail;
662 }
663 len = hostdot - hostname;
664 if(len > MAX_SIGV4_LEN) {
665 failf(data, "aws-sigv4: service too long in hostname");
666 result = CURLE_URL_MALFORMAT;
667 goto fail;
668 }
669 service.str = (char *)hostname;
670 service.len = len;
671
672 infof(data, "aws_sigv4: picked service %.*s from host",
673 (int)service.len, service.str);
674
675 if(!region.len) {
676 const char *reg = hostdot + 1;
677 const char *hostreg = strchr(reg, '.');
678 if(!hostreg) {
679 failf(data, "aws-sigv4: region missing in parameters and hostname");
680 result = CURLE_URL_MALFORMAT;
681 goto fail;
682 }
683 len = hostreg - reg;
684 if(len > MAX_SIGV4_LEN) {
685 failf(data, "aws-sigv4: region too long in hostname");
686 result = CURLE_URL_MALFORMAT;
687 goto fail;
688 }
689 region.str = (char *)reg;
690 region.len = len;
691 infof(data, "aws_sigv4: picked region %.*s from host",
692 (int)region.len, region.str);
693 }
694 }
695
696 Curl_http_method(data, conn, &method, &httpreq);
697
698 payload_hash = parse_content_sha_hdr(data, provider1.str, provider1.len,
699 &payload_hash_len);
700
701 if(!payload_hash) {
702 /* AWS S3 requires a x-amz-content-sha256 header, and supports special
703 * values like UNSIGNED-PAYLOAD */
704 bool sign_as_s3 = ((provider0.len == 3) &&
705 strncasecompare(provider0.str, "aws", 3)) &&
706 ((service.len == 2) && strncasecompare(service.str, "s3", 2));
707
708 if(sign_as_s3)
709 result = calc_s3_payload_hash(data, httpreq,
710 provider1.str, provider1.len,
711 sha_hash, sha_hex, content_sha256_hdr);
712 else
713 result = calc_payload_hash(data, sha_hash, sha_hex);
714 if(result)
715 goto fail;
716
717 payload_hash = sha_hex;
718 /* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */
719 payload_hash_len = strlen(sha_hex);
720 }
721
722 #ifdef DEBUGBUILD
723 {
724 char *force_timestamp = getenv("CURL_FORCETIME");
725 if(force_timestamp)
726 clock = 0;
727 else
728 clock = time(NULL);
729 }
730 #else
731 clock = time(NULL);
732 #endif
733 result = Curl_gmtime(clock, &tm);
734 if(result) {
735 goto fail;
736 }
737 if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
738 result = CURLE_OUT_OF_MEMORY;
739 goto fail;
740 }
741
742 result = make_headers(data, hostname, timestamp,
743 provider1.str, provider1.len,
744 &date_header, content_sha256_hdr,
745 &canonical_headers, &signed_headers);
746 if(result)
747 goto fail;
748
749 if(*content_sha256_hdr) {
750 /* make_headers() needed this without the \r\n for canonicalization */
751 size_t hdrlen = strlen(content_sha256_hdr);
752 DEBUGASSERT(hdrlen + 3 < sizeof(content_sha256_hdr));
753 memcpy(content_sha256_hdr + hdrlen, "\r\n", 3);
754 }
755
756 memcpy(date, timestamp, sizeof(date));
757 date[sizeof(date) - 1] = 0;
758
759 result = canon_query(data, data->state.up.query, &canonical_query);
760 if(result)
761 goto fail;
762
763 result = canon_string(data->state.up.path, strlen(data->state.up.path),
764 &canonical_path, NULL);
765 if(result)
766 goto fail;
767 result = CURLE_OUT_OF_MEMORY;
768
769 canonical_request =
770 aprintf("%s\n" /* HTTPRequestMethod */
771 "%s\n" /* CanonicalURI */
772 "%s\n" /* CanonicalQueryString */
773 "%s\n" /* CanonicalHeaders */
774 "%s\n" /* SignedHeaders */
775 "%.*s", /* HashedRequestPayload in hex */
776 method,
777 Curl_dyn_ptr(&canonical_path),
778 Curl_dyn_ptr(&canonical_query) ?
779 Curl_dyn_ptr(&canonical_query) : "",
780 Curl_dyn_ptr(&canonical_headers),
781 Curl_dyn_ptr(&signed_headers),
782 (int)payload_hash_len, payload_hash);
783 if(!canonical_request)
784 goto fail;
785
786 DEBUGF(infof(data, "Canonical request: %s", canonical_request));
787
788 request_type = aprintf("%.*s4_request", (int)provider0.len, provider0.str);
789 if(!request_type)
790 goto fail;
791
792 /* provider0 is lowercased *after* aprintf() so that the buffer can be
793 written to */
794 Curl_strntolower(request_type, request_type, provider0.len);
795
796 credential_scope = aprintf("%s/%.*s/%.*s/%s",
797 date, (int)region.len, region.str,
798 (int)service.len, service.str,
799 request_type);
800 if(!credential_scope)
801 goto fail;
802
803 if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request,
804 strlen(canonical_request)))
805 goto fail;
806
807 sha256_to_hex(sha_hex, sha_hash);
808
809 /*
810 * Google allows using RSA key instead of HMAC, so this code might change
811 * in the future. For now we only support HMAC.
812 */
813 str_to_sign = aprintf("%.*s4-HMAC-SHA256\n" /* Algorithm */
814 "%s\n" /* RequestDateTime */
815 "%s\n" /* CredentialScope */
816 "%s", /* HashedCanonicalRequest in hex */
817 (int)provider0.len, provider0.str,
818 timestamp,
819 credential_scope,
820 sha_hex);
821 if(!str_to_sign)
822 goto fail;
823
824 /* make provider0 part done uppercase */
825 Curl_strntoupper(str_to_sign, provider0.str, provider0.len);
826
827 secret = aprintf("%.*s4%s", (int)provider0.len, provider0.str,
828 data->state.aptr.passwd ?
829 data->state.aptr.passwd : "");
830 if(!secret)
831 goto fail;
832 /* make provider0 part done uppercase */
833 Curl_strntoupper(secret, provider0.str, provider0.len);
834
835 HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
836 HMAC_SHA256(sign0, sizeof(sign0), region.str, region.len, sign1);
837 HMAC_SHA256(sign1, sizeof(sign1), service.str, service.len, sign0);
838 HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
839 HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
840
841 sha256_to_hex(sha_hex, sign0);
842
843 auth_headers = aprintf("Authorization: %.*s4-HMAC-SHA256 "
844 "Credential=%s/%s, "
845 "SignedHeaders=%s, "
846 "Signature=%s\r\n"
847 /*
848 * date_header is added here, only if it was not
849 * user-specified (using CURLOPT_HTTPHEADER).
850 * date_header includes \r\n
851 */
852 "%s"
853 "%s", /* optional sha256 header includes \r\n */
854 (int)provider0.len, provider0.str,
855 user,
856 credential_scope,
857 Curl_dyn_ptr(&signed_headers),
858 sha_hex,
859 date_header ? date_header : "",
860 content_sha256_hdr);
861 if(!auth_headers) {
862 goto fail;
863 }
864 /* provider 0 uppercase */
865 Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1],
866 provider0.str, provider0.len);
867
868 Curl_safefree(data->state.aptr.userpwd);
869 data->state.aptr.userpwd = auth_headers;
870 data->state.authhost.done = TRUE;
871 result = CURLE_OK;
872
873 fail:
874 Curl_dyn_free(&canonical_query);
875 Curl_dyn_free(&canonical_path);
876 Curl_dyn_free(&canonical_headers);
877 Curl_dyn_free(&signed_headers);
878 free(canonical_request);
879 free(request_type);
880 free(credential_scope);
881 free(str_to_sign);
882 free(secret);
883 free(date_header);
884 return result;
885 }
886
887 #endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */
888