Coverage Report

Created: 2026-08-13 07:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/http_aws_sigv4.c
Line
Count
Source
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.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
#include "curl_setup.h"
25
26
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS)
27
28
#include "urldata.h"
29
#include "strcase.h"
30
#include "curlx/strdup.h"
31
#include "http_aws_sigv4.h"
32
#include "curl_sha256.h"
33
#include "transfer.h"
34
#include "curl_trc.h"
35
#include "escape.h"
36
#include "curlx/strparse.h"
37
#include "slist.h"
38
39
#include <time.h>
40
41
#define HMAC_SHA256(k, kl, d, dl, o)                 \
42
94.4k
  do {                                               \
43
94.4k
    result = Curl_hmacit(&Curl_HMAC_SHA256,          \
44
94.4k
                         (const unsigned char *)(k), \
45
94.4k
                         kl,                         \
46
94.4k
                         (const unsigned char *)(d), \
47
94.4k
                         dl, o);                     \
48
94.4k
    if(result) {                                     \
49
0
      goto fail;                                     \
50
0
    }                                                \
51
94.4k
  } while(0)
52
53
19.4k
#define TIMESTAMP_SIZE 17
54
55
/* hex-encoded with null-terminator */
56
56.7k
#define SHA256_HEX_LENGTH ((2 * CURL_SHA256_DIGEST_LENGTH) + 1)
57
58
49.4k
#define MAX_QUERY_COMPONENTS 128
59
60
struct pair {
61
  struct dynbuf key;
62
  struct dynbuf value;
63
};
64
65
static void sha256_to_hex(char *dst, unsigned char *sha)
66
56.6k
{
67
56.6k
  Curl_hexencode(sha, CURL_SHA256_DIGEST_LENGTH,
68
56.6k
                 (unsigned char *)dst, SHA256_HEX_LENGTH);
69
56.6k
}
70
71
static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr)
72
18.9k
{
73
18.9k
  char *tmp = Curl_checkheaders(data, sig_hdr, strlen(sig_hdr));
74
75
18.9k
  if(tmp)
76
281
    return tmp;
77
18.7k
  return Curl_checkheaders(data, STRCONST("Date"));
78
18.9k
}
79
80
/* remove whitespace, and lowercase all headers */
81
static void trim_headers(struct curl_slist *head)
82
18.9k
{
83
18.9k
  struct curl_slist *l;
84
196k
  for(l = head; l; l = l->next) {
85
177k
    const char *value; /* to read from */
86
177k
    char *store;
87
177k
    size_t colon = strcspn(l->data, ":");
88
177k
    Curl_strntolower(l->data, l->data, colon);
89
90
177k
    value = &l->data[colon];
91
177k
    if(!*value)
92
0
      continue;
93
177k
    ++value;
94
177k
    store = (char *)CURL_UNCONST(value);
95
96
    /* skip leading whitespace */
97
177k
    curlx_str_passblanks(&value);
98
99
3.42M
    while(*value) {
100
3.24M
      int space = 0;
101
3.31M
      while(ISBLANK(*value)) {
102
70.3k
        value++;
103
70.3k
        space++;
104
70.3k
      }
105
3.24M
      if(space) {
106
        /* replace any number of consecutive whitespace with a single space,
107
           unless at the end of the string, then nothing */
108
28.5k
        if(*value)
109
18.0k
          *store++ = ' ';
110
28.5k
      }
111
3.21M
      else
112
3.21M
        *store++ = *value++;
113
3.24M
    }
114
177k
    *store = 0; /* null-terminate */
115
177k
  }
116
18.9k
}
117
118
/*
119
 * Frees all allocated strings in a dynbuf pair array, and the dynbuf itself
120
 */
121
static void pair_array_free(struct pair *pair_array, size_t num_elements)
122
8.15k
{
123
8.15k
  size_t index;
124
125
55.1k
  for(index = 0; index != num_elements; index++) {
126
47.0k
    curlx_dyn_free(&pair_array[index].key);
127
47.0k
    curlx_dyn_free(&pair_array[index].value);
128
47.0k
  }
129
8.15k
}
130
131
/*
132
 * Frees all allocated strings in a split dynbuf, and the dynbuf itself
133
 */
134
static void dyn_array_free(struct dynbuf *db, size_t num_elements)
135
8.59k
{
136
8.59k
  size_t index;
137
138
58.0k
  for(index = 0; index < num_elements; index++)
139
49.4k
    curlx_dyn_free((&db[index]));
140
8.59k
}
141
142
/*
143
 * Splits source string by SPLIT_BY, and creates an array of dynbuf in db.
144
 * db is initialized by this function.
145
 * Caller is responsible for freeing the array elements with dyn_array_free
146
 */
147
148
6.61M
#define SPLIT_BY '&'
149
150
static CURLcode split_to_dyn_array(const char *source,
151
                                   struct dynbuf db[MAX_QUERY_COMPONENTS],
152
                                   size_t *num_splits_out)
153
8.59k
{
154
8.59k
  CURLcode result = CURLE_OK;
155
8.59k
  size_t len = strlen(source);
156
8.59k
  size_t pos;         /* Position in result buffer */
157
8.59k
  size_t start = 0;   /* Start of current segment */
158
8.59k
  size_t segment_length = 0;
159
8.59k
  size_t index = 0;
160
8.59k
  size_t num_splits = 0;
161
162
  /* Split source_ptr on SPLIT_BY and store the segment offsets and length in
163
   * array */
164
6.61M
  for(pos = 0; pos < len; pos++) {
165
6.61M
    if(source[pos] == SPLIT_BY) {
166
51.2k
      if(segment_length) {
167
41.6k
        curlx_dyn_init(&db[index], segment_length + 1);
168
41.6k
        result = curlx_dyn_addn(&db[index], &source[start], segment_length);
169
41.6k
        if(result)
170
0
          goto fail;
171
172
41.6k
        segment_length = 0;
173
41.6k
        index++;
174
41.6k
        if(++num_splits == MAX_QUERY_COMPONENTS) {
175
11
          result = CURLE_TOO_LARGE;
176
11
          goto fail;
177
11
        }
178
41.6k
      }
179
51.1k
      start = pos + 1;
180
51.1k
    }
181
6.55M
    else {
182
6.55M
      segment_length++;
183
6.55M
    }
184
6.61M
  }
185
186
8.57k
  if(segment_length) {
187
7.80k
    curlx_dyn_init(&db[index], segment_length + 1);
188
7.80k
    result = curlx_dyn_addn(&db[index], &source[start], segment_length);
189
7.80k
    if(!result) {
190
7.80k
      if(++num_splits == MAX_QUERY_COMPONENTS)
191
8
        result = CURLE_TOO_LARGE;
192
7.80k
    }
193
7.80k
  }
194
8.59k
fail:
195
8.59k
  *num_splits_out = num_splits;
196
8.59k
  return result;
197
8.57k
}
198
199
static bool is_reserved_char(const char c)
200
9.00M
{
201
9.00M
  return (ISALNUM(c) || ISURLPUNTCS(c));
202
9.00M
}
203
204
static CURLcode uri_encode_path(struct Curl_str *original_path,
205
                                struct dynbuf *new_path)
206
18.7k
{
207
18.7k
  const char *p = curlx_str(original_path);
208
18.7k
  size_t i;
209
210
4.32M
  for(i = 0; i < curlx_strlen(original_path); i++) {
211
    /* Do not encode slashes or unreserved chars from RFC 3986 */
212
4.30M
    CURLcode result = CURLE_OK;
213
4.30M
    unsigned char c = p[i];
214
4.30M
    if(is_reserved_char(c) || c == '/')
215
2.78M
      result = curlx_dyn_addn(new_path, &c, 1);
216
1.52M
    else
217
1.52M
      result = curlx_dyn_addf(new_path, "%%%02X", c);
218
4.30M
    if(result)
219
18
      return result;
220
4.30M
  }
221
222
18.7k
  return CURLE_OK;
223
18.7k
}
224
225
/* Normalize the query part. Make sure %2B is left percent encoded, and not
226
   decoded to plus, then encoded to space. */
227
static CURLcode normalize_query(const char *string, size_t len,
228
                                struct dynbuf *db)
229
61.6k
{
230
61.6k
  CURLcode result = CURLE_OK;
231
232
4.76M
  while(len && !result) {
233
4.70M
    unsigned char in = (unsigned char)*string;
234
4.70M
    if(('%' == in) && (len > 2) &&
235
1.20M
       ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
236
      /* this is two hexadecimal digits following a '%' */
237
790k
      in = (unsigned char)((curlx_hexval(string[1]) << 4) |
238
790k
                           curlx_hexval(string[2]));
239
790k
      string += 3;
240
790k
      len -= 3;
241
790k
      if(in == '+') {
242
        /* decodes to plus, so leave this encoded */
243
1.13k
        result = curlx_dyn_addn(db, "%2B", 3);
244
1.13k
        continue;
245
1.13k
      }
246
790k
    }
247
3.91M
    else {
248
3.91M
      string++;
249
3.91M
      len--;
250
3.91M
    }
251
252
4.70M
    if(is_reserved_char(in))
253
      /* Escape unreserved chars from RFC 3986 */
254
1.06M
      result = curlx_dyn_addn(db, &in, 1);
255
3.64M
    else if(in == '+')
256
      /* Encode '+' as space */
257
13.6k
      result = curlx_dyn_add(db, "%20");
258
3.62M
    else
259
3.62M
      result = curlx_dyn_addf(db, "%%%02X", in);
260
4.70M
  }
261
262
61.6k
  return result;
263
61.6k
}
264
265
static bool should_urlencode(struct Curl_str *service_name)
266
18.8k
{
267
  /*
268
   * These services require unmodified (not additionally URL-encoded) URL
269
   * paths.
270
   * should_urlencode == true is equivalent to should_urlencode_uri_path
271
   * from the AWS SDK. Urls are already normalized by the curl URL parser
272
   */
273
18.8k
  if(curlx_str_cmp(service_name, "s3") ||
274
18.7k
     curlx_str_cmp(service_name, "s3-express") ||
275
18.7k
     curlx_str_cmp(service_name, "s3-outposts")) {
276
179
    return FALSE;
277
179
  }
278
18.7k
  return TRUE;
279
18.8k
}
280
281
/* maximum length for the aws sivg4 parts */
282
110k
#define MAX_SIGV4_LEN    64
283
37.9k
#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date"))
284
285
/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */
286
18.9k
#define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1)
287
288
/* alphabetically compare two headers by their name, expecting
289
   headers to use ':' at this point */
290
static int compare_header_names(const char *a, const char *b)
291
107M
{
292
107M
  const char *colon_a;
293
107M
  const char *colon_b;
294
107M
  size_t len_a;
295
107M
  size_t len_b;
296
107M
  size_t min_len;
297
107M
  int cmp;
298
299
107M
  colon_a = strchr(a, ':');
300
107M
  colon_b = strchr(b, ':');
301
302
107M
  DEBUGASSERT(colon_a);
303
107M
  DEBUGASSERT(colon_b);
304
305
107M
  len_a = colon_a ? (size_t)(colon_a - a) : strlen(a);
306
107M
  len_b = colon_b ? (size_t)(colon_b - b) : strlen(b);
307
308
107M
  min_len = (len_a < len_b) ? len_a : len_b;
309
310
107M
  cmp = strncmp(a, b, min_len);
311
312
  /* return the shorter of the two if one is shorter */
313
107M
  if(!cmp)
314
92.4M
    return (int)(len_a - len_b);
315
316
15.5M
  return cmp;
317
107M
}
318
319
/* Merge duplicate header definitions by comma delimiting their values
320
   in the order defined the headers are defined, expecting headers to
321
   be alpha-sorted and use ':' at this point */
322
static CURLcode merge_duplicate_headers(struct curl_slist *head)
323
18.9k
{
324
18.9k
  struct curl_slist *curr = head;
325
18.9k
  CURLcode result = CURLE_OK;
326
327
195k
  while(curr) {
328
195k
    struct curl_slist *next = curr->next;
329
195k
    if(!next)
330
18.9k
      break;
331
332
176k
    if(compare_header_names(curr->data, next->data) == 0) {
333
136k
      struct dynbuf buf;
334
136k
      const char *colon_next;
335
136k
      const char *val_next;
336
337
136k
      curlx_dyn_init(&buf, CURL_MAX_HTTP_HEADER);
338
339
136k
      result = curlx_dyn_add(&buf, curr->data);
340
136k
      if(result)
341
0
        return result;
342
343
136k
      colon_next = strchr(next->data, ':');
344
136k
      DEBUGASSERT(colon_next);
345
136k
      val_next = colon_next + 1;
346
347
136k
      result = curlx_dyn_addn(&buf, ",", 1);
348
136k
      if(result)
349
0
        return result;
350
351
136k
      result = curlx_dyn_add(&buf, val_next);
352
136k
      if(result)
353
0
        return result;
354
355
136k
      curlx_free(curr->data);
356
136k
      curr->data = curlx_dyn_ptr(&buf);
357
358
136k
      curr->next = next->next;
359
136k
      curlx_free(next->data);
360
136k
      curlx_free(next);
361
136k
    }
362
40.4k
    else {
363
40.4k
      curr = curr->next;
364
40.4k
    }
365
176k
  }
366
367
18.9k
  return CURLE_OK;
368
18.9k
}
369
370
/* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */
371
static CURLcode make_headers(struct Curl_easy *data,
372
                             const char *hostname,
373
                             char *timestamp,
374
                             const char *provider1,
375
                             size_t plen, /* length of provider1 */
376
                             char **date_header,
377
                             char *content_sha256_header,
378
                             struct dynbuf *canonical_headers,
379
                             struct dynbuf *signed_headers)
380
18.9k
{
381
18.9k
  char date_hdr_key[DATE_HDR_KEY_LEN];
382
18.9k
  char date_full_hdr[DATE_FULL_HDR_LEN];
383
18.9k
  struct curl_slist *head = NULL;
384
18.9k
  struct curl_slist *tmp_head = NULL;
385
18.9k
  CURLcode result = CURLE_OUT_OF_MEMORY;
386
18.9k
  struct curl_slist *l;
387
18.9k
  bool again = TRUE;
388
389
18.9k
  curl_msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%.*s-Date",
390
18.9k
                 (int)plen, provider1);
391
  /* provider1 ucfirst */
392
18.9k
  Curl_strntolower(&date_hdr_key[2], provider1, plen);
393
18.9k
  date_hdr_key[2] = Curl_raw_toupper(provider1[0]);
394
395
18.9k
  curl_msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
396
18.9k
                 "x-%.*s-date:%s", (int)plen, provider1, timestamp);
397
  /* provider1 lowercase */
398
18.9k
  Curl_strntolower(&date_full_hdr[2], provider1, plen);
399
400
18.9k
  if(!Curl_checkheaders(data, STRCONST("Host"))) {
401
18.7k
    char *fullhost;
402
403
18.7k
    if(data->state.aptr.host)
404
2.88k
      fullhost = curlx_strdup(data->state.aptr.host);
405
15.8k
    else
406
15.8k
      fullhost = curl_maprintf("host:%s", hostname);
407
408
18.7k
    if(fullhost)
409
18.7k
      head = Curl_slist_append_nodup(NULL, fullhost);
410
18.7k
    if(!head) {
411
0
      curlx_free(fullhost);
412
0
      goto fail;
413
0
    }
414
18.7k
  }
415
416
18.9k
  if(*content_sha256_header) {
417
93
    tmp_head = curl_slist_append(head, content_sha256_header);
418
93
    if(!tmp_head)
419
0
      goto fail;
420
93
    head = tmp_head;
421
93
  }
422
423
  /* copy user headers to our header list. the logic is based on how http.c
424
     handles user headers.
425
426
     user headers in format 'name:' with no value are used to signal that an
427
     internal header of that name should be removed. those user headers are not
428
     added to this list.
429
430
     user headers in format 'name;' with no value are used to signal that a
431
     header of that name with no value should be sent. those user headers are
432
     added to this list but in the format that they will be sent, ie the
433
     semi-colon is changed to a colon for format 'name:'.
434
435
     user headers with a value of whitespace only, or without a colon or
436
     semi-colon, are not added to this list.
437
     */
438
196k
  for(l = data->set.headers; l; l = l->next) {
439
177k
    char *dupdata;
440
177k
    const char *ptr;
441
177k
    const char *sep = strchr(l->data, ':');
442
177k
    if(!sep)
443
95.8k
      sep = strchr(l->data, ';');
444
177k
    if(!sep || (*sep == ':' && !*(sep + 1)))
445
17.1k
      continue;
446
174k
    for(ptr = sep + 1; ISBLANK(*ptr); ++ptr)
447
14.5k
      ;
448
160k
    if(!*ptr && ptr != sep + 1) /* a value of whitespace only */
449
1.55k
      continue;
450
158k
    dupdata = curlx_strdup(l->data);
451
158k
    if(!dupdata)
452
0
      goto fail;
453
158k
    dupdata[sep - l->data] = ':';
454
158k
    tmp_head = Curl_slist_append_nodup(head, dupdata);
455
158k
    if(!tmp_head) {
456
0
      curlx_free(dupdata);
457
0
      goto fail;
458
0
    }
459
158k
    head = tmp_head;
460
158k
  }
461
462
18.9k
  trim_headers(head);
463
464
18.9k
  *date_header = find_date_hdr(data, date_hdr_key);
465
18.9k
  if(!*date_header) {
466
18.5k
    tmp_head = curl_slist_append(head, date_full_hdr);
467
18.5k
    if(!tmp_head)
468
0
      goto fail;
469
18.5k
    head = tmp_head;
470
18.5k
    *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp);
471
18.5k
    if(!*date_header)
472
0
      goto fail;
473
18.5k
  }
474
444
  else {
475
444
    const char *value;
476
444
    const char *endp;
477
444
    value = strchr(*date_header, ':');
478
444
    if(!value) {
479
35
      *date_header = NULL;
480
35
      goto fail;
481
35
    }
482
409
    ++value;
483
409
    curlx_str_passblanks(&value);
484
409
    endp = value;
485
7.86k
    while(*endp && ISALNUM(*endp))
486
7.45k
      ++endp;
487
    /* 16 bytes => "19700101T000000Z" */
488
409
    if((endp - value) == TIMESTAMP_SIZE - 1) {
489
22
      memcpy(timestamp, value, TIMESTAMP_SIZE - 1);
490
22
      timestamp[TIMESTAMP_SIZE - 1] = 0;
491
22
    }
492
387
    else
493
      /* bad timestamp length */
494
387
      timestamp[0] = 0;
495
409
    *date_header = NULL;
496
409
  }
497
498
  /* alpha-sort by header name in a case sensitive manner */
499
153k
  do {
500
153k
    again = FALSE;
501
108M
    for(l = head; l; l = l->next) {
502
107M
      struct curl_slist *next = l->next;
503
504
107M
      if(next && compare_header_names(l->data, next->data) > 0) {
505
13.5M
        char *tmp = l->data;
506
507
13.5M
        l->data = next->data;
508
13.5M
        next->data = tmp;
509
13.5M
        again = TRUE;
510
13.5M
      }
511
107M
    }
512
153k
  } while(again);
513
514
18.9k
  result = merge_duplicate_headers(head);
515
18.9k
  if(result)
516
0
    goto fail;
517
518
78.3k
  for(l = head; l; l = l->next) {
519
59.4k
    char *tmp;
520
521
59.4k
    if(curlx_dyn_add(canonical_headers, l->data))
522
13
      goto fail;
523
59.3k
    if(curlx_dyn_add(canonical_headers, "\n"))
524
1
      goto fail;
525
526
59.3k
    tmp = strchr(l->data, ':');
527
59.3k
    if(tmp)
528
59.3k
      *tmp = 0;
529
530
59.3k
    if(l != head) {
531
40.4k
      if(curlx_dyn_add(signed_headers, ";"))
532
0
        goto fail;
533
40.4k
    }
534
59.3k
    if(curlx_dyn_add(signed_headers, l->data))
535
0
      goto fail;
536
59.3k
  }
537
538
18.9k
  result = CURLE_OK;
539
18.9k
fail:
540
18.9k
  curl_slist_free_all(head);
541
542
18.9k
  return result;
543
18.9k
}
544
545
93
#define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256"))
546
/* add 2 for ": " between header name and value */
547
93
#define CONTENT_SHA256_HDR_LEN (CONTENT_SHA256_KEY_LEN + 2 + SHA256_HEX_LENGTH)
548
549
/* try to parse a payload hash from the content-sha256 header */
550
static const char *parse_content_sha_hdr(struct Curl_easy *data,
551
                                         const char *provider1,
552
                                         size_t plen,
553
                                         size_t *value_len)
554
18.9k
{
555
18.9k
  char key[CONTENT_SHA256_KEY_LEN];
556
18.9k
  size_t key_len;
557
18.9k
  const char *value;
558
18.9k
  size_t len;
559
560
18.9k
  key_len = curl_msnprintf(key, sizeof(key), "x-%.*s-content-sha256",
561
18.9k
                           (int)plen, provider1);
562
563
18.9k
  value = Curl_checkheaders(data, key, key_len);
564
18.9k
  if(!value)
565
18.9k
    return NULL;
566
567
56
  value = strchr(value, ':');
568
56
  if(!value)
569
10
    return NULL;
570
46
  ++value;
571
572
46
  curlx_str_passblanks(&value);
573
574
46
  len = strlen(value);
575
622
  while(len > 0 && ISBLANK(value[len - 1]))
576
576
    --len;
577
578
46
  *value_len = len;
579
46
  return value;
580
56
}
581
582
static CURLcode calc_payload_hash(struct Curl_easy *data,
583
                                  unsigned char *sha_hash, char *sha_hex)
584
18.9k
{
585
18.9k
  const char *post_data = data->set.postfields;
586
18.9k
  size_t post_data_len = 0;
587
18.9k
  CURLcode result;
588
589
18.9k
  if(post_data) {
590
940
    if(data->set.postfieldsize < 0)
591
940
      post_data_len = strlen(post_data);
592
0
    else
593
0
      post_data_len = (size_t)data->set.postfieldsize;
594
940
  }
595
18.9k
  result = Curl_sha256it(sha_hash, (const unsigned char *)post_data,
596
18.9k
                         post_data_len);
597
18.9k
  if(!result)
598
18.9k
    sha256_to_hex(sha_hex, sha_hash);
599
18.9k
  return result;
600
18.9k
}
601
602
22
#define S3_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD"
603
604
static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
605
                                     Curl_HttpReq httpreq,
606
                                     const char *provider1,
607
                                     size_t plen,
608
                                     unsigned char *sha_hash,
609
                                     char *sha_hex, char *header)
610
93
{
611
93
  bool empty_method = (httpreq == HTTPREQ_GET || httpreq == HTTPREQ_HEAD);
612
  /* The request method or filesize indicate no request payload */
613
93
  bool empty_payload = (empty_method || data->set.filesize == 0);
614
  /* The POST payload is in memory */
615
93
  bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields);
616
93
  CURLcode result = CURLE_OUT_OF_MEMORY;
617
618
93
  if(empty_payload || post_payload) {
619
    /* Calculate a real hash when we know the request payload */
620
71
    result = calc_payload_hash(data, sha_hash, sha_hex);
621
71
    if(result)
622
0
      goto fail;
623
71
  }
624
22
  else {
625
    /* Fall back to s3's UNSIGNED-PAYLOAD */
626
22
    size_t len = CURL_CSTRLEN(S3_UNSIGNED_PAYLOAD);
627
22
    DEBUGASSERT(len < SHA256_HEX_LENGTH); /* 16 < 65 */
628
22
    memcpy(sha_hex, S3_UNSIGNED_PAYLOAD, len);
629
22
    sha_hex[len] = 0;
630
22
  }
631
632
  /* format the required content-sha256 header */
633
93
  curl_msnprintf(header, CONTENT_SHA256_HDR_LEN,
634
93
                 "x-%.*s-content-sha256: %s", (int)plen, provider1, sha_hex);
635
636
93
  result = CURLE_OK;
637
93
fail:
638
93
  return result;
639
93
}
640
641
static int compare_func(const void *a, const void *b)
642
150k
{
643
150k
  const struct pair *aa = a;
644
150k
  const struct pair *bb = b;
645
150k
  const size_t aa_key_len = curlx_dyn_len(&aa->key);
646
150k
  const size_t bb_key_len = curlx_dyn_len(&bb->key);
647
150k
  const size_t aa_value_len = curlx_dyn_len(&aa->value);
648
150k
  const size_t bb_value_len = curlx_dyn_len(&bb->value);
649
150k
  int compare;
650
651
  /* If one element is empty, the other is always sorted higher */
652
653
  /* Compare keys */
654
150k
  if((aa_key_len == 0) && (bb_key_len == 0))
655
15.8k
    return 0;
656
134k
  if(aa_key_len == 0)
657
3.93k
    return -1;
658
130k
  if(bb_key_len == 0)
659
19.8k
    return 1;
660
110k
  compare = strcmp(curlx_dyn_ptr(&aa->key), curlx_dyn_ptr(&bb->key));
661
110k
  if(compare) {
662
84.6k
    return compare;
663
84.6k
  }
664
665
  /* Compare values */
666
25.9k
  if((aa_value_len == 0) && (bb_value_len == 0))
667
0
    return 0;
668
25.9k
  if(aa_value_len == 0)
669
0
    return -1;
670
25.9k
  if(bb_value_len == 0)
671
0
    return 1;
672
25.9k
  compare = strcmp(curlx_dyn_ptr(&aa->value), curlx_dyn_ptr(&bb->value));
673
674
25.9k
  return compare;
675
25.9k
}
676
677
/* @unittest 1979 */
678
UNITTEST CURLcode canon_path(const char *q, size_t len,
679
                             struct dynbuf *new_path,
680
                             bool do_uri_encode);
681
UNITTEST CURLcode canon_path(const char *q, size_t len,
682
                             struct dynbuf *new_path,
683
                             bool do_uri_encode)
684
18.8k
{
685
18.8k
  CURLcode result = CURLE_OK;
686
687
18.8k
  struct Curl_str original_path;
688
689
18.8k
  curlx_str_assign(&original_path, q, len);
690
691
  /* Normalized path will be either the same or shorter than the original
692
   * path, plus trailing slash */
693
694
18.8k
  if(do_uri_encode)
695
18.7k
    result = uri_encode_path(&original_path, new_path);
696
179
  else
697
179
    result = curlx_dyn_addn(new_path, q, len);
698
699
18.8k
  if(!result) {
700
18.8k
    if(curlx_dyn_len(new_path) == 0)
701
0
      result = curlx_dyn_add(new_path, "/");
702
18.8k
  }
703
704
18.8k
  return result;
705
18.8k
}
706
707
/* @unittest 1980 */
708
UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq);
709
UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq)
710
18.9k
{
711
18.9k
  CURLcode result = CURLE_OK;
712
713
18.9k
  struct dynbuf query_array[MAX_QUERY_COMPONENTS];
714
18.9k
  struct pair encoded_query_array[MAX_QUERY_COMPONENTS];
715
18.9k
  size_t num_query_components;
716
18.9k
  size_t counted_query_components = 0;
717
18.9k
  size_t index;
718
719
18.9k
  if(!query)
720
10.3k
    return result;
721
722
8.59k
  result = split_to_dyn_array(query, &query_array[0], &num_query_components);
723
8.59k
  if(result) {
724
19
    goto fail;
725
19
  }
726
727
  /* Create list of pairs, each pair containing an encoded query
728
   * component */
729
730
55.5k
  for(index = 0; index < num_query_components; index++) {
731
47.0k
    const char *in_key;
732
47.0k
    size_t in_key_len;
733
47.0k
    const char *offset;
734
47.0k
    size_t query_part_len = curlx_dyn_len(&query_array[index]);
735
47.0k
    const char *query_part = curlx_dyn_ptr(&query_array[index]);
736
737
47.0k
    in_key = query_part;
738
739
47.0k
    offset = strchr(query_part, '=');
740
    /* If there is no equals, this key has no value */
741
47.0k
    if(!offset) {
742
27.4k
      in_key_len = strlen(in_key);
743
27.4k
    }
744
19.6k
    else {
745
19.6k
      in_key_len = offset - in_key;
746
19.6k
    }
747
748
47.0k
    curlx_dyn_init(&encoded_query_array[index].key,
749
47.0k
      (query_part_len * 3) + 1);
750
47.0k
    curlx_dyn_init(&encoded_query_array[index].value,
751
47.0k
      (query_part_len * 3) + 1);
752
47.0k
    counted_query_components++;
753
754
    /* Decode/encode the key */
755
47.0k
    result = normalize_query(in_key, in_key_len,
756
47.0k
                             &encoded_query_array[index].key);
757
47.0k
    if(result) {
758
0
      goto fail;
759
0
    }
760
761
    /* Decode/encode the value if it exists */
762
47.0k
    if(offset && offset != (query_part + query_part_len - 1)) {
763
14.6k
      size_t in_value_len;
764
14.6k
      const char *in_value = offset + 1;
765
14.6k
      in_value_len = query_part + query_part_len - (offset + 1);
766
14.6k
      result = normalize_query(in_value, in_value_len,
767
14.6k
                               &encoded_query_array[index].value);
768
14.6k
      if(result) {
769
0
        goto fail;
770
0
      }
771
14.6k
    }
772
32.3k
    else {
773
      /* If there is no value, the value is an empty string */
774
32.3k
      curlx_dyn_init(&encoded_query_array[index].value, 2);
775
32.3k
      result = curlx_dyn_addn(&encoded_query_array[index].value, "", 1);
776
32.3k
    }
777
778
47.0k
    if(result) {
779
0
      goto fail;
780
0
    }
781
47.0k
  }
782
783
  /* Sort the encoded query components by key and value */
784
8.57k
  qsort(&encoded_query_array, num_query_components,
785
8.57k
        sizeof(struct pair), compare_func);
786
787
  /* Append the query components together to make a full query string */
788
55.1k
  for(index = 0; index < num_query_components; index++) {
789
790
46.6k
    if(index)
791
38.5k
      result = curlx_dyn_addn(dq, "&", 1);
792
46.6k
    if(!result) {
793
46.6k
      const char *key_ptr = curlx_dyn_ptr(&encoded_query_array[index].key);
794
46.6k
      const char *value_ptr = curlx_dyn_ptr(&encoded_query_array[index].value);
795
46.6k
      size_t vlen = curlx_dyn_len(&encoded_query_array[index].value);
796
46.6k
      if(value_ptr && vlen) {
797
46.6k
        result = curlx_dyn_addf(dq, "%s=%s", key_ptr, value_ptr);
798
46.6k
      }
799
0
      else {
800
        /* Empty value is always encoded to key= */
801
0
        result = curlx_dyn_addf(dq, "%s=", key_ptr);
802
0
      }
803
46.6k
    }
804
46.6k
    if(result)
805
35
      break;
806
46.6k
  }
807
808
8.59k
fail:
809
8.59k
  if(counted_query_components)
810
    /* the encoded_query_array might not be initialized yet */
811
8.15k
    pair_array_free(&encoded_query_array[0], counted_query_components);
812
8.59k
  dyn_array_free(&query_array[0], num_query_components);
813
8.59k
  return result;
814
8.57k
}
815
816
static CURLcode parse_sigv4_params(struct Curl_easy *data,
817
                                   const char *hostname,
818
                                   struct Curl_str *provider0,
819
                                   struct Curl_str *provider1,
820
                                   struct Curl_str *region,
821
                                   struct Curl_str *service)
822
19.1k
{
823
19.1k
  const char *line = data->set.str[STRING_AWS_SIGV4];
824
19.1k
  if(!line || !*line)
825
13.7k
    line = "aws:amz";
826
827
  /* provider0[:provider1[:region[:service]]]
828
829
     No string can be longer than N bytes of non-whitespace */
830
19.1k
  if(curlx_str_until(&line, provider0, MAX_SIGV4_LEN, ':')) {
831
18
    failf(data, "first aws-sigv4 provider cannot be empty");
832
18
    return CURLE_BAD_FUNCTION_ARGUMENT;
833
18
  }
834
19.0k
  if(curlx_str_single(&line, ':') ||
835
14.7k
     curlx_str_until(&line, provider1, MAX_SIGV4_LEN, ':')) {
836
4.49k
    *provider1 = *provider0;
837
4.49k
  }
838
14.6k
  else if(curlx_str_single(&line, ':') ||
839
651
          curlx_str_until(&line, region, MAX_SIGV4_LEN, ':') ||
840
607
          curlx_str_single(&line, ':') ||
841
14.1k
          curlx_str_until(&line, service, MAX_SIGV4_LEN, ':')) {
842
    /* nothing to do */
843
14.1k
  }
844
845
19.0k
  if(!curlx_strlen(service)) {
846
18.6k
    const char *p = hostname;
847
18.6k
    if(curlx_str_until(&p, service, MAX_SIGV4_LEN, '.') ||
848
18.6k
       curlx_str_single(&p, '.')) {
849
66
      failf(data, "aws-sigv4: service missing in parameters and hostname");
850
66
      return CURLE_URL_MALFORMAT;
851
66
    }
852
853
18.5k
    infof(data, "aws_sigv4: picked service %.*s from host",
854
18.5k
          (int)curlx_strlen(service), curlx_str(service));
855
856
18.5k
    if(!curlx_strlen(region)) {
857
18.4k
      if(curlx_str_until(&p, region, MAX_SIGV4_LEN, '.') ||
858
18.4k
         curlx_str_single(&p, '.')) {
859
43
        failf(data, "aws-sigv4: region missing in parameters and hostname");
860
43
        return CURLE_URL_MALFORMAT;
861
43
      }
862
18.3k
      infof(data, "aws_sigv4: picked region %.*s from host",
863
18.3k
            (int)curlx_strlen(region), curlx_str(region));
864
18.3k
    }
865
18.5k
  }
866
867
18.9k
  return CURLE_OK;
868
19.0k
}
869
870
static CURLcode get_payload_hash(struct Curl_easy *data,
871
                                 Curl_HttpReq httpreq,
872
                                 struct Curl_str *provider0,
873
                                 struct Curl_str *provider1,
874
                                 struct Curl_str *service,
875
                                 unsigned char *sha_hash,
876
                                 char *sha_hex,
877
                                 char *content_sha256_hdr,
878
                                 const char **payload_hash_out,
879
                                 size_t *payload_hash_len_out)
880
18.9k
{
881
18.9k
  *payload_hash_out =
882
18.9k
    parse_content_sha_hdr(data, curlx_str(provider1),
883
18.9k
                          curlx_strlen(provider1), payload_hash_len_out);
884
885
18.9k
  if(!*payload_hash_out) {
886
18.9k
    CURLcode result;
887
    /* AWS S3 requires a x-amz-content-sha256 header, and supports special
888
     * values like UNSIGNED-PAYLOAD */
889
18.9k
    bool sign_as_s3 = curlx_str_casecompare(provider0, "aws") &&
890
13.6k
                      curlx_str_casecompare(service, "s3");
891
892
18.9k
    if(sign_as_s3)
893
93
      result = calc_s3_payload_hash(data, httpreq, curlx_str(provider1),
894
93
                                    curlx_strlen(provider1), sha_hash,
895
93
                                    sha_hex, content_sha256_hdr);
896
18.8k
    else
897
18.8k
      result = calc_payload_hash(data, sha_hash, sha_hex);
898
18.9k
    if(result)
899
0
      return result;
900
901
18.9k
    *payload_hash_out = sha_hex;
902
    /* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */
903
18.9k
    *payload_hash_len_out = strlen(sha_hex);
904
18.9k
  }
905
18.9k
  return CURLE_OK;
906
18.9k
}
907
908
static CURLcode get_timestamp(char *timestamp, size_t stampsize)
909
18.9k
{
910
18.9k
  time_t clock;
911
18.9k
  struct tm tm;
912
18.9k
  CURLcode result;
913
914
18.9k
#ifdef DEBUGBUILD
915
18.9k
  {
916
18.9k
    char *force_timestamp = getenv("CURL_FORCETIME");
917
18.9k
    if(force_timestamp)
918
0
      clock = 0;
919
18.9k
    else
920
18.9k
      clock = time(NULL);
921
18.9k
  }
922
#else
923
  clock = time(NULL);
924
#endif
925
18.9k
  result = curlx_gmtime(clock, &tm);
926
18.9k
  if(result)
927
0
    return result;
928
929
18.9k
  if(!strftime(timestamp, stampsize, "%Y%m%dT%H%M%SZ", &tm))
930
0
    return CURLE_OUT_OF_MEMORY;
931
932
18.9k
  return CURLE_OK;
933
18.9k
}
934
935
static CURLcode make_canonical_request(struct Curl_easy *data,
936
                                       const char *hostname,
937
                                       char *timestamp,
938
                                       struct Curl_str *provider1,
939
                                       struct Curl_str *service,
940
                                       const char *method,
941
                                       const char *payload_hash,
942
                                       size_t payload_hash_len,
943
                                       char **date_header_out,
944
                                       char *content_sha256_hdr,
945
                                       struct dynbuf *canonical_headers,
946
                                       struct dynbuf *signed_headers,
947
                                       char **canonical_request_out)
948
18.9k
{
949
18.9k
  struct dynbuf canonical_query;
950
18.9k
  struct dynbuf canonical_path;
951
18.9k
  CURLcode result;
952
953
18.9k
  curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
954
18.9k
  curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER);
955
956
18.9k
  result = make_headers(data, hostname, timestamp,
957
18.9k
                        curlx_str(provider1), curlx_strlen(provider1),
958
18.9k
                        date_header_out, content_sha256_hdr,
959
18.9k
                        canonical_headers, signed_headers);
960
18.9k
  if(result)
961
35
    goto fail;
962
963
18.9k
  result = canon_query(data->state.up.query, &canonical_query);
964
18.9k
  if(result)
965
54
    goto fail;
966
967
18.8k
  result = canon_path(data->state.up.path, strlen(data->state.up.path),
968
18.8k
                      &canonical_path,
969
18.8k
                      should_urlencode(service));
970
18.8k
  if(result)
971
18
    goto fail;
972
973
18.8k
  *canonical_request_out =
974
18.8k
    curl_maprintf("%s\n" /* HTTPRequestMethod */
975
18.8k
                  "%s\n" /* CanonicalURI */
976
18.8k
                  "%s\n" /* CanonicalQueryString */
977
18.8k
                  "%s\n" /* CanonicalHeaders */
978
18.8k
                  "%s\n" /* SignedHeaders */
979
18.8k
                  "%.*s",  /* HashedRequestPayload in hex */
980
18.8k
                  method,
981
18.8k
                  curlx_dyn_ptr(&canonical_path),
982
18.8k
                  curlx_dyn_ptr(&canonical_query) ?
983
10.7k
                  curlx_dyn_ptr(&canonical_query) : "",
984
18.8k
                  curlx_dyn_ptr(canonical_headers),
985
18.8k
                  curlx_dyn_ptr(signed_headers),
986
18.8k
                  (int)payload_hash_len, payload_hash);
987
18.8k
  if(!*canonical_request_out) {
988
0
    result = CURLE_OUT_OF_MEMORY;
989
0
    goto fail;
990
0
  }
991
992
18.8k
  result = CURLE_OK;
993
18.9k
fail:
994
18.9k
  curlx_dyn_free(&canonical_query);
995
18.9k
  curlx_dyn_free(&canonical_path);
996
18.9k
  return result;
997
18.8k
}
998
999
static CURLcode make_string_to_sign(struct Curl_easy *data,
1000
                                    struct Curl_str *provider0,
1001
                                    struct Curl_str *region,
1002
                                    struct Curl_str *service,
1003
                                    const char *date,
1004
                                    const char *timestamp,
1005
                                    const char *canonical_request,
1006
                                    char **request_type_out,
1007
                                    char **credential_scope_out,
1008
                                    char **str_to_sign_out)
1009
18.8k
{
1010
18.8k
  char *request_type;
1011
18.8k
  char *credential_scope;
1012
18.8k
  char *str_to_sign;
1013
18.8k
  unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH];
1014
18.8k
  char sha_hex[SHA256_HEX_LENGTH];
1015
1016
18.8k
  request_type = curl_maprintf("%.*s4_request",
1017
18.8k
                               (int)curlx_strlen(provider0),
1018
18.8k
                               curlx_str(provider0));
1019
18.8k
  if(!request_type)
1020
0
    return CURLE_OUT_OF_MEMORY;
1021
1022
  /* provider0 is lowercased *after* curl_maprintf() so that the buffer
1023
     can be written to */
1024
18.8k
  Curl_strntolower(request_type, request_type, curlx_strlen(provider0));
1025
1026
18.8k
  credential_scope = curl_maprintf("%s/%.*s/%.*s/%s", date,
1027
18.8k
                                   (int)curlx_strlen(region),
1028
18.8k
                                   curlx_str(region),
1029
18.8k
                                   (int)curlx_strlen(service),
1030
18.8k
                                   curlx_str(service),
1031
18.8k
                                   request_type);
1032
18.8k
  if(!credential_scope) {
1033
0
    curlx_free(request_type);
1034
0
    return CURLE_OUT_OF_MEMORY;
1035
0
  }
1036
1037
18.8k
  if(Curl_sha256it(sha_hash, (const unsigned char *)canonical_request,
1038
18.8k
                   strlen(canonical_request))) {
1039
0
    curlx_free(request_type);
1040
0
    curlx_free(credential_scope);
1041
0
    return CURLE_OUT_OF_MEMORY;
1042
0
  }
1043
1044
18.8k
  sha256_to_hex(sha_hex, sha_hash);
1045
1046
  /*
1047
   * Google allows using RSA key instead of HMAC, so this code might change
1048
   * in the future. For now we only support HMAC.
1049
   */
1050
18.8k
  str_to_sign = curl_maprintf("%.*s4-HMAC-SHA256\n" /* Algorithm */
1051
18.8k
                              "%s\n" /* RequestDateTime */
1052
18.8k
                              "%s\n" /* CredentialScope */
1053
18.8k
                              "%s",  /* HashedCanonicalRequest in hex */
1054
18.8k
                              (int)curlx_strlen(provider0),
1055
18.8k
                              curlx_str(provider0),
1056
18.8k
                              timestamp,
1057
18.8k
                              credential_scope,
1058
18.8k
                              sha_hex);
1059
18.8k
  if(!str_to_sign) {
1060
0
    curlx_free(request_type);
1061
0
    curlx_free(credential_scope);
1062
0
    return CURLE_OUT_OF_MEMORY;
1063
0
  }
1064
1065
  /* make provider0 part done uppercase */
1066
18.8k
  Curl_strntoupper(str_to_sign, curlx_str(provider0),
1067
18.8k
                   curlx_strlen(provider0));
1068
1069
18.8k
  infof(data, "aws_sigv4: String to sign (enclosed in []) - [%s]",
1070
18.8k
        str_to_sign);
1071
1072
18.8k
  *request_type_out = request_type;
1073
18.8k
  *credential_scope_out = credential_scope;
1074
18.8k
  *str_to_sign_out = str_to_sign;
1075
18.8k
  return CURLE_OK;
1076
18.8k
}
1077
1078
static CURLcode sign_and_set_auth_headers(struct Curl_easy *data,
1079
                                          struct Curl_str *provider0,
1080
                                          struct Curl_str *region,
1081
                                          struct Curl_str *service,
1082
                                          const char *request_type,
1083
                                          const char *credential_scope,
1084
                                          const char *date,
1085
                                          const char *str_to_sign,
1086
                                          const char *date_header,
1087
                                          const char *content_sha256_hdr,
1088
                                          struct dynbuf *signed_headers)
1089
18.8k
{
1090
18.8k
  CURLcode result = CURLE_OUT_OF_MEMORY;
1091
18.8k
  const char *passwd = Curl_creds_passwd(data->state.creds);
1092
18.8k
  char *secret = NULL;
1093
18.8k
  unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 };
1094
18.8k
  unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 };
1095
18.8k
  char sha_hex[SHA256_HEX_LENGTH];
1096
18.8k
  char *auth_headers = NULL;
1097
18.8k
  char *user = curl_escape(Curl_creds_user(data->state.creds), 0);
1098
18.8k
  if(!user)
1099
0
    return CURLE_OUT_OF_MEMORY;
1100
1101
18.8k
  secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(provider0),
1102
18.8k
                         curlx_str(provider0), passwd);
1103
18.8k
  if(!secret)
1104
0
    goto fail;
1105
  /* make provider0 part done uppercase */
1106
18.8k
  Curl_strntoupper(secret, curlx_str(provider0), curlx_strlen(provider0));
1107
1108
18.8k
  HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
1109
18.8k
  HMAC_SHA256(sign0, sizeof(sign0),
1110
18.8k
              curlx_str(region), curlx_strlen(region), sign1);
1111
18.8k
  HMAC_SHA256(sign1, sizeof(sign1),
1112
18.8k
              curlx_str(service), curlx_strlen(service), sign0);
1113
18.8k
  HMAC_SHA256(sign0, sizeof(sign0),
1114
18.8k
              request_type, strlen(request_type), sign1);
1115
18.8k
  HMAC_SHA256(sign1, sizeof(sign1),
1116
18.8k
              str_to_sign, strlen(str_to_sign), sign0);
1117
1118
18.8k
  sha256_to_hex(sha_hex, sign0);
1119
1120
18.8k
  infof(data, "aws_sigv4: Signature - %s", sha_hex);
1121
1122
18.8k
  auth_headers = curl_maprintf("Authorization: %.*s4-HMAC-SHA256 "
1123
18.8k
                               "Credential=%s/%s, "
1124
18.8k
                               "SignedHeaders=%s, "
1125
18.8k
                               "Signature=%s\r\n"
1126
18.8k
                               "%s"
1127
18.8k
                               "%s%s",
1128
18.8k
                               (int)curlx_strlen(provider0),
1129
18.8k
                               curlx_str(provider0),
1130
18.8k
                               user,
1131
18.8k
                               credential_scope,
1132
18.8k
                               curlx_dyn_ptr(signed_headers),
1133
18.8k
                               sha_hex,
1134
                               /*
1135
                                * date_header is added here, only if it was not
1136
                                * user-specified (using CURLOPT_HTTPHEADER).
1137
                                * date_header includes \r\n
1138
                                */
1139
18.8k
                               date_header ? date_header : "",
1140
18.8k
                               content_sha256_hdr,
1141
18.8k
                               content_sha256_hdr[0] ? "\r\n": "");
1142
18.8k
  if(!auth_headers)
1143
0
    goto fail;
1144
1145
  /* provider 0 uppercase */
1146
18.8k
  Curl_strntoupper(&auth_headers[CURL_CSTRLEN("Authorization: ")],
1147
18.8k
                   curlx_str(provider0), curlx_strlen(provider0));
1148
1149
18.8k
  curlx_free(data->req.hd_auth);
1150
18.8k
  data->req.hd_auth = auth_headers;
1151
18.8k
  data->state.authhost.done = TRUE;
1152
18.8k
  result = CURLE_OK;
1153
1154
18.8k
fail:
1155
18.8k
  curlx_free(user);
1156
18.8k
  curlx_free(secret);
1157
18.8k
  return result;
1158
18.8k
}
1159
1160
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
1161
19.1k
{
1162
19.1k
  CURLcode result = CURLE_OUT_OF_MEMORY;
1163
19.1k
  struct Curl_str provider0 = { NULL, 0 };
1164
19.1k
  struct Curl_str provider1 = { NULL, 0 };
1165
19.1k
  struct Curl_str region = { NULL, 0 };
1166
19.1k
  struct Curl_str service = { NULL, 0 };
1167
19.1k
  const char *hostname = data->state.origin->hostname;
1168
19.1k
  char timestamp[TIMESTAMP_SIZE];
1169
19.1k
  char date[9];
1170
19.1k
  struct dynbuf canonical_headers;
1171
19.1k
  struct dynbuf signed_headers;
1172
19.1k
  char *date_header = NULL;
1173
19.1k
  Curl_HttpReq httpreq;
1174
19.1k
  const char *method = NULL;
1175
19.1k
  const char *payload_hash = NULL;
1176
19.1k
  size_t payload_hash_len = 0;
1177
19.1k
  unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH];
1178
19.1k
  char sha_hex[SHA256_HEX_LENGTH];
1179
19.1k
  char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */
1180
19.1k
  char *canonical_request = NULL;
1181
19.1k
  char *request_type = NULL;
1182
19.1k
  char *credential_scope = NULL;
1183
19.1k
  char *str_to_sign = NULL;
1184
1185
19.1k
  if(data->set.path_as_is) {
1186
13
    failf(data, "Cannot use sigv4 authentication with path-as-is flag");
1187
13
    return CURLE_BAD_FUNCTION_ARGUMENT;
1188
13
  }
1189
1190
19.1k
  if(Curl_checkheaders(data, STRCONST("Authorization")))
1191
    /* Authorization already present, Bailing out */
1192
14
    return CURLE_OK;
1193
1194
  /* we init those buffers here, so goto fail will free initialized dynbuf */
1195
19.1k
  curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
1196
19.1k
  curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
1197
1198
19.1k
  result = parse_sigv4_params(data, hostname, &provider0, &provider1,
1199
19.1k
                              &region, &service);
1200
19.1k
  if(!result) {
1201
18.9k
    Curl_http_method(data, &method, &httpreq);
1202
18.9k
    result = get_payload_hash(data, httpreq, &provider0, &provider1, &service,
1203
18.9k
                              sha_hash, sha_hex, content_sha256_hdr,
1204
18.9k
                              &payload_hash, &payload_hash_len);
1205
18.9k
  }
1206
1207
19.1k
  if(!result)
1208
18.9k
    result = get_timestamp(timestamp, sizeof(timestamp));
1209
1210
19.1k
  if(!result)
1211
18.9k
    result = make_canonical_request(data, hostname, timestamp,
1212
18.9k
                                    &provider1, &service,
1213
18.9k
                                    method, payload_hash, payload_hash_len,
1214
18.9k
                                    &date_header, content_sha256_hdr,
1215
18.9k
                                    &canonical_headers, &signed_headers,
1216
18.9k
                                    &canonical_request);
1217
19.1k
  if(!result) {
1218
    /* the timestamp might have been updated in make_canonical_request */
1219
18.8k
    memcpy(date, timestamp, sizeof(date) - 1);
1220
18.8k
    date[sizeof(date) - 1] = 0;
1221
1222
18.8k
    result = make_string_to_sign(data, &provider0, &region, &service,
1223
18.8k
                                 date, timestamp, canonical_request,
1224
18.8k
                                 &request_type, &credential_scope,
1225
18.8k
                                 &str_to_sign);
1226
18.8k
  }
1227
19.1k
  if(!result)
1228
18.8k
    result = sign_and_set_auth_headers(data, &provider0, &region, &service,
1229
18.8k
                                       request_type, credential_scope,
1230
18.8k
                                       date, str_to_sign, date_header,
1231
18.8k
                                       content_sha256_hdr, &signed_headers);
1232
1233
19.1k
  curlx_dyn_free(&canonical_headers);
1234
19.1k
  curlx_dyn_free(&signed_headers);
1235
19.1k
  curlx_free(canonical_request);
1236
19.1k
  curlx_free(request_type);
1237
19.1k
  curlx_free(credential_scope);
1238
19.1k
  curlx_free(str_to_sign);
1239
19.1k
  curlx_free(date_header);
1240
19.1k
  return result;
1241
19.1k
}
1242
1243
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_AWS */