Coverage Report

Created: 2023-01-10 06:17

/src/fluent-bit/src/flb_signv4.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*  Fluent Bit
4
 *  ==========
5
 *  Copyright (C) 2015-2022 The Fluent Bit Authors
6
 *
7
 *  Licensed under the Apache License, Version 2.0 (the "License");
8
 *  you may not use this file except in compliance with the License.
9
 *  You may obtain a copy of the License at
10
 *
11
 *      http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 *  Unless required by applicable law or agreed to in writing, software
14
 *  distributed under the License is distributed on an "AS IS" BASIS,
15
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 *  See the License for the specific language governing permissions and
17
 *  limitations under the License.
18
 */
19
20
/*
21
 *
22
 * AWS Signv4 documentation
23
 *
24
 * https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
25
 */
26
27
#include <fluent-bit/flb_info.h>
28
#include <fluent-bit/flb_kv.h>
29
#include <fluent-bit/flb_sds.h>
30
#include <fluent-bit/flb_utils.h>
31
#include <fluent-bit/flb_hmac.h>
32
#include <fluent-bit/flb_hash.h>
33
#include <fluent-bit/flb_http_client.h>
34
#include <fluent-bit/flb_signv4.h>
35
#include <fluent-bit/flb_aws_credentials.h>
36
37
#include <stdlib.h>
38
#include <ctype.h>
39
40
static flb_sds_t sha256_to_hex(unsigned char *sha256)
41
0
{
42
0
    int i;
43
0
    flb_sds_t hex;
44
0
    flb_sds_t tmp;
45
46
0
    hex = flb_sds_create_size(64);
47
0
    if (!hex) {
48
0
        flb_error("[signv4] cannot allocate buffer to convert sha256 to hex");
49
0
        return NULL;
50
0
    }
51
52
0
    for (i = 0; i < 32; i++) {
53
0
        tmp = flb_sds_printf(&hex, "%02x", sha256[i]);
54
0
        if (!tmp) {
55
0
            flb_error("[signedv4] error formatting sha256 -> hex");
56
0
            flb_sds_destroy(hex);
57
0
            return NULL;
58
0
        }
59
0
        hex = tmp;
60
0
    }
61
62
0
    return hex;
63
0
}
64
65
static int hmac_sha256_sign(unsigned char out[32],
66
                            unsigned char *key, size_t key_len,
67
                            unsigned char *msg, size_t msg_len)
68
0
{
69
0
    int result;
70
71
0
    result = flb_hmac_simple(FLB_HASH_SHA256,
72
0
                             key, key_len,
73
0
                             msg, msg_len,
74
0
                             out, 32);
75
76
0
    if (result != FLB_CRYPTO_SUCCESS) {
77
0
        return -1;
78
0
    }
79
80
0
    return 0;
81
0
}
82
83
static int kv_key_cmp(const void *a_arg, const void *b_arg)
84
0
{
85
0
    int ret;
86
0
    struct flb_kv *kv_a = *(struct flb_kv **) a_arg;
87
0
    struct flb_kv *kv_b = *(struct flb_kv **) b_arg;
88
89
0
    ret = strcmp(kv_a->key, kv_b->key);
90
0
    if (ret == 0) {
91
        /*
92
         * NULL pointer is allowed in kv_a->val and kv_b->val.
93
         * Handle NULL pointer cases before passing to strcmp.
94
         */
95
0
        if (kv_a->val == NULL && kv_b->val == NULL) {
96
0
            ret = 0;
97
0
        }
98
0
        else if (kv_a->val == NULL) {
99
0
            ret = -1;
100
0
        }
101
0
        else if (kv_b->val == NULL) {
102
0
            ret = 1;
103
0
        }
104
0
        else {
105
0
            ret = strcmp(kv_a->val, kv_b->val);
106
0
        }
107
0
    }
108
109
0
    return ret;
110
0
}
111
112
static inline int to_encode(char c)
113
0
{
114
0
    if ((c >= 48 && c <= 57)  ||  /* 0-9 */
115
0
        (c >= 65 && c <= 90)  ||  /* A-Z */
116
0
        (c >= 97 && c <= 122) ||  /* a-z */
117
0
        (c == '-' || c == '_' || c == '.' || c == '~' || c == '/' ||
118
0
         c == '=')) {
119
0
        return FLB_FALSE;
120
0
    }
121
122
0
    return FLB_TRUE;
123
0
}
124
125
static inline int to_encode_path(char c)
126
0
{
127
0
    if ((c >= 48 && c <= 57)  ||  /* 0-9 */
128
0
        (c >= 65 && c <= 90)  ||  /* A-Z */
129
0
        (c >= 97 && c <= 122) ||  /* a-z */
130
0
        (c == '-' || c == '_' || c == '.' || c == '~' || c == '/')) {
131
0
        return FLB_FALSE;
132
0
    }
133
134
0
    return FLB_TRUE;
135
0
}
136
137
flb_sds_t flb_signv4_uri_normalize_path(char *uri, size_t len)
138
0
{
139
0
    char *p;
140
0
    int end_slash = FLB_FALSE;
141
0
    struct mk_list *tmp;
142
0
    struct mk_list *prev;
143
0
    struct mk_list *head;
144
0
    struct mk_list *split;
145
0
    struct flb_split_entry *entry;
146
0
    flb_sds_t out;
147
148
0
    if (len == 0) {
149
0
        return NULL;
150
0
    }
151
152
0
    out = flb_sds_create_len(uri, len+1);
153
0
    if (!out) {
154
0
        return NULL;
155
0
    }
156
0
    out[len] = '\0';
157
158
0
    if (uri[len - 1] == '/') {
159
0
        end_slash = FLB_TRUE;
160
0
    }
161
162
0
    split = flb_utils_split(out, '/', -1);
163
0
    if (!split) {
164
0
        flb_sds_destroy(out);
165
0
        return NULL;
166
0
    }
167
168
0
    p = out;
169
0
    *p++ = '/';
170
171
0
    mk_list_foreach_safe(head, tmp, split) {
172
0
        entry = mk_list_entry(head, struct flb_split_entry, _head);
173
0
        if (entry->len == 1 && *entry->value == '.') {
174
0
            flb_utils_split_free_entry(entry);
175
0
        }
176
0
        else if (entry->len == 2 && memcmp(entry->value, "..", 2) == 0) {
177
0
            prev = head->prev;
178
0
            if (prev != split) {
179
0
                entry = mk_list_entry(prev, struct flb_split_entry, _head);
180
0
                flb_utils_split_free_entry(entry);
181
0
            }
182
0
            entry = mk_list_entry(head, struct flb_split_entry, _head);
183
0
            flb_utils_split_free_entry(entry);
184
0
        }
185
0
    }
186
187
0
    mk_list_foreach(head, split) {
188
0
        entry = mk_list_entry(head, struct flb_split_entry, _head);
189
0
        memcpy(p, entry->value, entry->len);
190
0
        p += entry->len;
191
192
0
        if (head->next != split) {
193
0
            *p++ = '/';
194
0
        }
195
0
    }
196
197
0
    len = (p - out);
198
0
    if (end_slash == FLB_TRUE && out[len - 1] != '/') {
199
0
        *p++ = '/';
200
0
    }
201
202
0
    flb_utils_split_free(split);
203
204
0
    flb_sds_len_set(out, p - out);
205
0
    out[p - out] = '\0';
206
207
0
    return out;
208
0
}
209
210
static flb_sds_t uri_encode(const char *uri, size_t len)
211
0
{
212
0
    int i;
213
0
    flb_sds_t buf = NULL;
214
0
    flb_sds_t tmp = NULL;
215
0
    int is_query_string = FLB_FALSE;
216
0
    int do_encode = FLB_FALSE;
217
218
0
    buf = flb_sds_create_size(len * 2);
219
0
    if (!buf) {
220
0
        flb_error("[signv4] cannot allocate buffer for URI encoding");
221
0
        return NULL;
222
0
    }
223
224
0
    for (i = 0; i < len; i++) {
225
0
        if (uri[i] == '?') {
226
0
            is_query_string = FLB_TRUE;
227
0
        }
228
0
        do_encode = FLB_FALSE;
229
230
0
        if (is_query_string == FLB_FALSE && to_encode_path(uri[i]) == FLB_TRUE) {
231
0
            do_encode = FLB_TRUE;
232
0
        }
233
0
        if (is_query_string == FLB_TRUE && to_encode(uri[i]) == FLB_TRUE) {
234
0
            do_encode = FLB_TRUE;
235
0
        }
236
0
        if (do_encode == FLB_TRUE) {
237
0
            tmp = flb_sds_printf(&buf, "%%%02X", (unsigned char) *(uri + i));
238
0
            if (!tmp) {
239
0
                flb_error("[signv4] error formatting special character");
240
0
                flb_sds_destroy(buf);
241
0
                return NULL;
242
0
            }
243
0
            buf = tmp;
244
0
            continue;
245
0
        }
246
247
        /* Direct assignment, just copy the character */
248
0
        if (buf) {
249
0
            tmp = flb_sds_cat(buf, uri + i, 1);
250
0
            if (!tmp) {
251
0
                flb_error("[signv4] error composing outgoing buffer");
252
0
                flb_sds_destroy(buf);
253
0
                return NULL;
254
0
            }
255
0
            buf = tmp;
256
0
        }
257
0
    }
258
259
0
    return buf;
260
0
}
261
262
/*
263
 * Encodes URI parameters, which can not have "/" characters in them
264
 * (This happens in an STS request, the role ARN has a slash and is
265
 * given as a query parameter).
266
 */
267
static flb_sds_t uri_encode_params(const char *uri, size_t len)
268
0
{
269
0
    int i;
270
0
    flb_sds_t buf = NULL;
271
0
    flb_sds_t tmp = NULL;
272
273
0
    buf = flb_sds_create_size(len * 2);
274
0
    if (!buf) {
275
0
        flb_error("[signv4] cannot allocate buffer for URI encoding");
276
0
        return NULL;
277
0
    }
278
279
0
    for (i = 0; i < len; i++) {
280
0
        if (to_encode(uri[i]) == FLB_TRUE || uri[i] == '/') {
281
0
            tmp = flb_sds_printf(&buf, "%%%02X", (unsigned char) *(uri + i));
282
0
            if (!tmp) {
283
0
                flb_error("[signv4] error formatting special character");
284
0
                flb_sds_destroy(buf);
285
0
                return NULL;
286
0
            }
287
0
            continue;
288
0
        }
289
290
        /* Direct assignment, just copy the character */
291
0
        if (buf) {
292
0
            tmp = flb_sds_cat(buf, uri + i, 1);
293
0
            if (!tmp) {
294
0
                flb_error("[signv4] error composing outgoing buffer");
295
0
                flb_sds_destroy(buf);
296
0
                return NULL;
297
0
            }
298
0
            buf = tmp;
299
0
        }
300
0
    }
301
302
0
    return buf;
303
0
}
304
305
/*
306
 * Convert URL encoded params (query string or POST payload) to a sorted
307
 * key/value linked list
308
 */
309
static flb_sds_t url_params_format(char *params)
310
0
{
311
0
    int i;
312
0
    int ret;
313
0
    int len;
314
0
    int items;
315
0
    char *p;
316
0
    struct mk_list list;
317
0
    struct mk_list split;
318
0
    struct mk_list *h_tmp;
319
0
    struct mk_list *head;
320
0
    struct flb_slist_entry *e;
321
0
    flb_sds_t key;
322
0
    flb_sds_t val;
323
0
    flb_sds_t tmp;
324
0
    flb_sds_t buf = NULL;
325
0
    struct flb_kv *kv;
326
0
    struct flb_kv **arr;
327
328
0
    mk_list_init(&list);
329
0
    mk_list_init(&split);
330
331
0
    ret = flb_slist_split_string(&split, params, '&', -1);
332
0
    if (ret == -1) {
333
0
        flb_error("[signv4] error processing given query string");
334
0
        return NULL;
335
0
    }
336
337
0
    mk_list_foreach_safe(head, h_tmp, &split) {
338
0
        e = mk_list_entry(head, struct flb_slist_entry, _head);
339
0
        p = strchr(e->str, '=');
340
0
        if (!p) {
341
0
            continue;
342
0
        }
343
344
0
        len = (p - e->str);
345
0
        p++;
346
347
        /* URI encode every key and value */
348
0
        key = uri_encode_params(e->str, len);
349
0
        len++;
350
0
        val = uri_encode_params(p, flb_sds_len(e->str) - len);
351
0
        if (!key || !val) {
352
0
            flb_error("[signv4] error encoding uri for query string");
353
0
            if (key) {
354
0
                flb_sds_destroy(key);
355
0
            }
356
0
            if (val) {
357
0
                flb_sds_destroy(val);
358
0
            }
359
0
            flb_slist_destroy(&split);
360
0
            flb_kv_release(&list);
361
0
            return NULL;
362
0
        }
363
364
        /*
365
         * If key length is 0 then a problem occurs because val
366
         * will not be set flb_kv_item_create_len, which eventually
367
         * results in issues since kv->val will be equal to NULL.
368
         * Thus, check here whether key length is satisfied
369
         */
370
0
        if (flb_sds_len(key) == 0) {
371
0
            flb_sds_destroy(key);
372
0
            flb_sds_destroy(val);
373
0
            flb_slist_destroy(&split);
374
0
            flb_kv_release(&list);
375
0
            return NULL;
376
0
        }
377
378
0
        kv = flb_kv_item_create_len(&list,
379
0
                                    key, flb_sds_len(key),
380
0
                                    val, flb_sds_len(val));
381
0
        flb_sds_destroy(key);
382
0
        flb_sds_destroy(val);
383
384
0
        if (!kv) {
385
0
            flb_error("[signv4] error processing key/value from query string");
386
0
            flb_slist_destroy(&split);
387
0
            flb_kv_release(&list);
388
0
            return NULL;
389
0
        }
390
0
    }
391
0
    flb_slist_destroy(&split);
392
393
    /* Sort the kv list of parameters */
394
0
    items = mk_list_size(&list);
395
0
    if (items == 0) {
396
0
        flb_kv_release(&list);
397
0
        return flb_sds_create("");
398
0
    }
399
400
0
    arr = flb_malloc(sizeof(struct flb_kv *) * items);
401
0
    if (!arr) {
402
0
        flb_errno();
403
0
        flb_kv_release(&list);
404
0
        return NULL;
405
0
    }
406
407
0
    i = 0;
408
0
    mk_list_foreach(head, &list) {
409
0
        kv = mk_list_entry(head, struct flb_kv, _head);
410
0
        arr[i] = kv;
411
0
        i++;
412
0
    }
413
    /* sort headers by key */
414
0
    qsort(arr, items, sizeof(struct flb_kv *), kv_key_cmp);
415
416
    /* Format query string parameters */
417
0
    buf = flb_sds_create_size(items * 64);
418
0
    if (!buf) {
419
0
        flb_kv_release(&list);
420
0
        flb_free(arr);
421
0
        return NULL;
422
0
    }
423
424
0
    for (i = 0; i < items; i++) {
425
0
        kv = (struct flb_kv *) arr[i];
426
0
        if (i + 1 < items) {
427
0
            if (kv->val == NULL) {
428
0
                tmp = flb_sds_printf(&buf, "%s=&",
429
0
                                     kv->key);
430
0
            }
431
0
            else {
432
0
                tmp = flb_sds_printf(&buf, "%s=%s&",
433
0
                                     kv->key, kv->val);
434
0
            }
435
0
        } 
436
0
        else {
437
0
            if (kv->val == NULL) {
438
0
                tmp = flb_sds_printf(&buf, "%s=",
439
0
                                     kv->key);
440
0
            }
441
0
            else {
442
0
                tmp = flb_sds_printf(&buf, "%s=%s",
443
0
                                     kv->key, kv->val);
444
0
            }
445
0
        }
446
0
        if (!tmp) {
447
0
            flb_error("[signv4] error allocating value");
448
0
        }
449
0
        buf = tmp;
450
0
    }
451
452
0
    flb_kv_release(&list);
453
0
    flb_free(arr);
454
455
0
    return buf;
456
0
}
457
458
/*
459
 * Given an original list of kv headers with 'in_list' as the list headed,
460
 * generate new entries on 'out_list' considering lower case headers key,
461
 * sorted by keys and values and merged duplicates.
462
 */
463
void headers_sanitize(struct mk_list *in_list, struct mk_list *out_list)
464
0
{
465
0
    int x;
466
0
    char *v_start;
467
0
    char *v_end;
468
0
    char *val;
469
0
    struct mk_list *head;
470
0
    struct mk_list *c_head;
471
0
    struct mk_list *tmp;
472
0
    struct mk_list out_tmp;
473
0
    struct flb_kv *kv;
474
0
    struct flb_kv *c_kv;
475
0
    flb_sds_t t;
476
477
0
    mk_list_init(&out_tmp);
478
479
    /* Create lowercase key headers in the temporal list */
480
0
    mk_list_foreach(head, in_list) {
481
0
        kv = mk_list_entry(head, struct flb_kv, _head);
482
483
        /* Sanitize value */
484
0
        v_start = kv->val;
485
0
        v_end = kv->val + flb_sds_len(kv->val);
486
0
        while (*v_start == ' ' || *v_start == '\t') {
487
0
            v_start++;
488
0
        }
489
0
        while (*v_end == ' ' || *v_end == '\t') {
490
0
            v_end--;
491
0
        }
492
493
        /*
494
         * The original headers might have upper case characters, for safety just
495
         * make a copy of them so we can lowercase them if required.
496
         */
497
0
        kv = flb_kv_item_create_len(&out_tmp,
498
0
                                    kv->key, flb_sds_len(kv->key),
499
0
                                    v_start, v_end - v_start);
500
0
        for (x = 0; x < flb_sds_len(kv->key); x++) {
501
0
            kv->key[x] = tolower(kv->key[x]);
502
0
        }
503
504
        /*
505
         * trim: kv->val alreay have a copy of the original value, now we need
506
         * to look for double empty spaces in the middle of the value and do
507
         * proper adjustments.
508
         */
509
0
        val = kv->val;
510
0
        while (v_start < v_end) {
511
0
            if (*v_start == ' ') {
512
0
                if (v_start < v_end && *(v_start + 1) == ' ') {
513
0
                    v_start++;
514
0
                    continue;
515
0
                }
516
0
            }
517
0
            *val = *v_start;
518
0
            v_start++;
519
0
            val++;
520
0
        }
521
0
        *val = '\0';
522
0
        flb_sds_len_set(kv->val, val - kv->val);
523
0
    }
524
525
    /* Find and merge duplicates */
526
0
    mk_list_foreach_safe(head, tmp, &out_tmp) {
527
0
        kv = mk_list_entry(head, struct flb_kv, _head);
528
529
        /* Check if this kv exists in out_list */
530
0
        c_kv = NULL;
531
0
        mk_list_foreach(c_head, out_list) {
532
0
            c_kv = mk_list_entry(c_head, struct flb_kv, _head);
533
0
            if (strcmp(kv->key, c_kv->key) == 0) {
534
0
                break;
535
0
            }
536
0
            c_kv = NULL;
537
0
        }
538
539
        /* if c_kv is set, means the key already exists in the outgoing list */
540
0
        if (c_kv) {
541
0
            t = flb_sds_printf(&c_kv->val, ",%s", kv->val);
542
0
            c_kv->val = t;
543
0
            flb_kv_item_destroy(kv);
544
0
        }
545
0
        else {
546
0
            mk_list_del(&kv->_head);
547
0
            mk_list_add(&kv->_head, out_list);
548
0
        }
549
0
    }
550
0
}
551
552
/*
553
 * Task 1: Create a canonical request
554
 * ==================================
555
 *
556
 *  https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
557
 *
558
 *  CanonicalRequest =
559
 *  HTTPRequestMethod + '\n' +
560
 *  CanonicalURI + '\n' +
561
 *  CanonicalQueryString + '\n' +
562
 *  CanonicalHeaders + '\n' +
563
 *  SignedHeaders + '\n' +
564
 *  HexEncode(Hash(RequestPayload))
565
 */
566
static flb_sds_t flb_signv4_canonical_request(struct flb_http_client *c,
567
                                              int normalize_uri,
568
                                              int amz_date_header,
569
                                              char *amzdate,
570
                                              char *security_token,
571
                                              int s3_mode,
572
                                              flb_sds_t *signed_headers)
573
0
{
574
0
    int i;
575
0
    int len;
576
0
    int items;
577
0
    int post_params = FLB_FALSE;
578
0
    int result;
579
0
    size_t size;
580
0
    char *val;
581
0
    struct flb_kv **arr;
582
0
    flb_sds_t cr;
583
0
    flb_sds_t uri;
584
0
    flb_sds_t tmp = NULL;
585
0
    flb_sds_t params = NULL;
586
0
    flb_sds_t payload_hash = NULL;
587
0
    struct flb_kv *kv;
588
0
    struct mk_list list_tmp;
589
0
    struct mk_list *head;
590
0
    unsigned char sha256_buf[64] = {0};
591
592
    /* Size hint */
593
0
    size = strlen(c->uri) + (mk_list_size(&c->headers) * 64) + 256;
594
595
0
    cr = flb_sds_create_size(size);
596
0
    if (!cr) {
597
0
        flb_error("[signv4] cannot allocate buffer");
598
0
        return NULL;
599
0
    }
600
601
0
    switch (c->method) {
602
0
    case FLB_HTTP_GET:
603
0
        tmp = flb_sds_cat(cr, "GET\n", 4);
604
0
        break;
605
0
    case FLB_HTTP_POST:
606
0
        tmp = flb_sds_cat(cr, "POST\n", 5);
607
0
        break;
608
0
    case FLB_HTTP_PUT:
609
0
        tmp = flb_sds_cat(cr, "PUT\n", 4);
610
0
        break;
611
0
    case FLB_HTTP_HEAD:
612
0
        tmp = flb_sds_cat(cr, "HEAD\n", 5);
613
0
        break;
614
0
    };
615
616
0
    if (!tmp) {
617
0
        flb_error("[signv4] invalid processing of HTTP method");
618
0
        flb_sds_destroy(cr);
619
0
        return NULL;
620
0
    }
621
622
0
    cr = tmp;
623
624
    /* Our URI already contains the query string, so do the proper adjustments */
625
0
    if (c->query_string) {
626
0
        len = (c->query_string - c->uri) - 1;
627
0
    }
628
0
    else {
629
0
        len = strlen(c->uri);
630
0
    }
631
632
    /*
633
     * URI normalization is required by certain AWS service, for hence the caller
634
     * plugin is responsible to enable/disable this flag. If set the URI in the
635
     * canonical request will be normalized.
636
     */
637
0
    if (normalize_uri == FLB_TRUE) {
638
0
        tmp = flb_signv4_uri_normalize_path((char *) c->uri, len);
639
0
        if (!tmp) {
640
0
            flb_error("[signv4] error normalizing path");
641
0
            flb_sds_destroy(cr);
642
0
            return NULL;
643
0
        }
644
0
        len = flb_sds_len(tmp);
645
0
    }
646
0
    else {
647
0
        tmp = (char *) c->uri;
648
0
    }
649
650
    /* Do URI encoding (rfc3986) */
651
0
    uri = uri_encode(tmp, len);
652
0
    if (tmp != c->uri) {
653
0
        flb_sds_destroy(tmp);
654
0
    }
655
0
    if (!uri) {
656
        /* error composing outgoing buffer */
657
0
        flb_sds_destroy(cr);
658
0
        return NULL;
659
0
    }
660
661
0
    tmp = flb_sds_cat(cr, uri, flb_sds_len(uri));
662
0
    if (!tmp) {
663
0
        flb_error("[signv4] error concatenating encoded URI");
664
0
        flb_sds_destroy(uri);
665
0
        flb_sds_destroy(cr);
666
0
        return NULL;
667
0
    }
668
0
    cr = tmp;
669
0
    flb_sds_destroy(uri);
670
671
0
    tmp = flb_sds_cat(cr, "\n", 1);
672
0
    if (!tmp) {
673
0
        flb_error("[signv4] error concatenating encoded URI break line");
674
0
        flb_sds_destroy(cr);
675
0
        return NULL;
676
0
    }
677
0
    cr = tmp;
678
679
    /* Canonical Query String */
680
0
    tmp = NULL;
681
0
    if (c->query_string) {
682
0
        params = url_params_format((char *) c->query_string);
683
0
        if (!params) {
684
0
            flb_sds_destroy(cr);
685
0
            return NULL;
686
0
        }
687
0
        tmp = flb_sds_cat(cr, params, flb_sds_len(params));
688
0
        if (!tmp) {
689
0
            flb_error("[signv4] error concatenating query string");
690
0
            flb_sds_destroy(params);
691
0
            flb_sds_destroy(cr);
692
0
            return NULL;
693
0
        }
694
0
        flb_sds_destroy(params);
695
0
        cr = tmp;
696
0
    }
697
698
    /*
699
     * If the original HTTP method is POST and we have some urlencoded parameters
700
     * as payload, we must handle them as we did for the query string.
701
     */
702
0
    if (c->method == FLB_HTTP_POST && c->body_len > 0) {
703
0
        val = (char *) flb_kv_get_key_value("Content-Type", &c->headers);
704
0
        if (val) {
705
0
            if (strstr(val, "application/x-www-form-urlencoded")) {
706
0
                params = url_params_format((char *) c->body_buf);
707
0
                if (!params) {
708
0
                    flb_error("[signv4] error processing POST payload params");
709
0
                    flb_sds_destroy(cr);
710
0
                    return NULL;
711
0
                }
712
0
                tmp = flb_sds_cat(cr, params, flb_sds_len(params));
713
0
                if (!tmp) {
714
0
                    flb_error("[signv4] error concatenating POST payload params");
715
0
                    flb_sds_destroy(params);
716
0
                    flb_sds_destroy(cr);
717
0
                    return NULL;
718
0
                }
719
0
                cr = tmp;
720
0
                flb_sds_destroy(params);
721
0
                post_params = FLB_TRUE;
722
0
            }
723
0
        }
724
0
    }
725
726
    /* query string / POST separator */
727
0
    tmp = flb_sds_cat(cr, "\n", 1);
728
0
    if (!tmp) {
729
0
        flb_error("[signv4] error adding params breakline separator");
730
0
        flb_sds_destroy(cr);
731
0
        return NULL;
732
0
    }
733
0
    cr = tmp;
734
735
    /*
736
     * Calculate payload hash.
737
     * This is added at the end of all canonical requests, unless
738
     * S3_MODE_UNSIGNED_PAYLOAD is set.
739
     * If we're using S3_MODE_SIGNED_PAYLOAD, then the hash is added to the
740
     * canonical headers.
741
     */
742
0
    if (s3_mode == S3_MODE_UNSIGNED_PAYLOAD) {
743
0
        payload_hash = flb_sds_create("UNSIGNED-PAYLOAD");
744
0
    } else {
745
0
        if (c->body_len > 0 && post_params == FLB_FALSE) {
746
0
            result = flb_hash_simple(FLB_HASH_SHA256,
747
0
                                     (unsigned char *) c->body_buf, c->body_len,
748
0
                                     sha256_buf, sizeof(sha256_buf));
749
0
        }
750
0
        else {
751
0
            result = flb_hash_simple(FLB_HASH_SHA256,
752
0
                                     (unsigned char *) NULL, 0,
753
0
                                     sha256_buf, sizeof(sha256_buf));
754
0
        }
755
756
0
        if (result != FLB_CRYPTO_SUCCESS) {
757
0
            flb_error("[signv4] error hashing payload");
758
0
            flb_sds_destroy(cr);
759
0
            return NULL;
760
0
        }
761
762
0
        payload_hash = flb_sds_create_size(64);
763
0
        if (!payload_hash) {
764
0
            flb_error("[signv4] error formatting hashed payload");
765
0
            flb_sds_destroy(cr);
766
0
            return NULL;
767
0
        }
768
0
        for (i = 0; i < 32; i++) {
769
0
            tmp = flb_sds_printf(&payload_hash, "%02x",
770
0
                                 (unsigned char) sha256_buf[i]);
771
0
            if (!tmp) {
772
0
                flb_error("[signv4] error formatting hashed payload");
773
0
                flb_sds_destroy(cr);
774
0
                flb_sds_destroy(payload_hash);
775
0
                return NULL;
776
0
            }
777
0
            payload_hash = tmp;
778
0
        }
779
0
    }
780
781
    /*
782
     * Canonical Headers
783
     *
784
     * Add the required custom headers:
785
     *
786
     * - x-amz-date
787
     * - x-amz-security-token (if set)
788
     * - x-amz-content-sha256 (if S3_MODE_SIGNED_PAYLOAD)
789
     */
790
0
    mk_list_init(&list_tmp);
791
792
    /* include x-amz-date header ? */
793
0
    if (amz_date_header == FLB_TRUE) {
794
0
        len = strlen(amzdate);
795
0
        flb_http_add_header(c, "x-amz-date", 10, amzdate, len);
796
0
    }
797
798
    /* x-amz-security_token */
799
0
    if (security_token) {
800
0
        len = strlen(security_token);
801
0
        flb_http_add_header(c, "x-amz-security-token", 20, security_token, len);
802
0
    }
803
804
0
    if (s3_mode == S3_MODE_SIGNED_PAYLOAD) {
805
0
        flb_http_add_header(c, "x-amz-content-sha256", 20, payload_hash, 64);
806
0
    }
807
808
0
    headers_sanitize(&c->headers, &list_tmp);
809
810
    /*
811
     * For every header registered, append it to the temporal array so we can sort them
812
     * later.
813
     */
814
0
    items = mk_list_size(&list_tmp);
815
0
    size = (sizeof(struct flb_kv *) * items);
816
0
    arr = flb_malloc(size);
817
0
    if (!arr) {
818
0
        flb_errno();
819
0
        flb_kv_release(&list_tmp);
820
0
        flb_sds_destroy(cr);
821
0
        flb_sds_destroy(payload_hash);
822
0
        return NULL;
823
0
    }
824
825
    /* Compose temporal array to sort headers */
826
0
    i = 0;
827
0
    mk_list_foreach(head, &list_tmp) {
828
0
        kv = mk_list_entry(head, struct flb_kv, _head);
829
0
        arr[i] = kv;
830
0
        i++;
831
0
    }
832
833
    /* Sort the headers from the temporal array */
834
0
    qsort(arr, items, sizeof(struct flb_kv *), kv_key_cmp);
835
836
    /* Iterate sorted headers and append them to the outgoing buffer */
837
0
    for (i = 0; i < items; i++) {
838
0
        kv = (struct flb_kv *) arr[i];
839
0
        tmp = flb_sds_printf(&cr, "%s:%s\n", kv->key, kv->val);
840
0
        if (!tmp) {
841
0
            flb_error("[signv4] error composing canonical headers");
842
0
            flb_free(arr);
843
0
            flb_kv_release(&list_tmp);
844
0
            flb_sds_destroy(cr);
845
0
            flb_sds_destroy(payload_hash);
846
0
            return NULL;
847
0
        }
848
0
        cr = tmp;
849
0
    }
850
851
    /* Add required breakline */
852
0
    tmp = flb_sds_printf(&cr, "\n");
853
0
    if (!tmp) {
854
0
        flb_error("[signv4] error adding extra breakline separator");
855
0
        flb_free(arr);
856
0
        flb_kv_release(&list_tmp);
857
0
        flb_sds_destroy(cr);
858
0
        flb_sds_destroy(payload_hash);
859
0
        return NULL;
860
0
    }
861
0
    cr = tmp;
862
863
    /* Signed Headers for canonical request context */
864
0
    for (i = 0; i < items; i++) {
865
0
        kv = (struct flb_kv *) arr[i];
866
867
        /* Check if this is the last header, if so add breakline separator */
868
0
        if (i + 1 == items) {
869
0
            tmp = flb_sds_printf(&cr, "%s\n", kv->key);
870
0
        }
871
0
        else {
872
0
            tmp = flb_sds_printf(&cr, "%s;", kv->key);
873
0
        }
874
0
        if (!tmp) {
875
0
            flb_error("[signv4] error composing canonical signed headers");
876
0
            flb_free(arr);
877
0
            flb_kv_release(&list_tmp);
878
0
            flb_sds_destroy(cr);
879
0
            flb_sds_destroy(payload_hash);
880
0
            return NULL;
881
0
        }
882
0
        cr = tmp;
883
0
    }
884
885
    /* Signed Headers for authorization header (Task 4) */
886
0
    for (i = 0; i < items; i++) {
887
0
        kv = (struct flb_kv *) arr[i];
888
889
        /* Check if this is the last header, if so add breakline separator */
890
0
        if (i + 1 == items) {
891
0
            tmp = flb_sds_printf(signed_headers, "%s", kv->key);
892
0
        }
893
0
        else {
894
0
            tmp = flb_sds_printf(signed_headers, "%s;", kv->key);
895
0
        }
896
0
        if (!tmp) {
897
0
            flb_error("[signv4] error composing auth signed headers");
898
0
            flb_free(arr);
899
0
            flb_kv_release(&list_tmp);
900
0
            flb_sds_destroy(cr);
901
0
            flb_sds_destroy(payload_hash);
902
0
            return NULL;
903
0
        }
904
0
        *signed_headers = tmp;
905
0
    }
906
907
0
    flb_free(arr);
908
0
    flb_kv_release(&list_tmp);
909
910
    /* Add Payload Hash */
911
0
    tmp = flb_sds_printf(&cr, "%s", payload_hash);
912
0
    if (!tmp) {
913
0
        flb_error("[signv4] error adding payload hash");
914
0
        flb_sds_destroy(cr);
915
0
        flb_sds_destroy(payload_hash);
916
0
        return NULL;
917
0
    }
918
0
    cr = tmp;
919
0
    flb_sds_destroy(payload_hash);
920
921
0
    return cr;
922
0
}
923
924
/*
925
 * Task 2
926
 * ======
927
 * https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
928
 */
929
static flb_sds_t flb_signv4_string_to_sign(struct flb_http_client *c,
930
                                           flb_sds_t cr, char *amzdate,
931
                                           char *datestamp, char *service,
932
                                           char *region)
933
0
{
934
0
    int i;
935
0
    int result;
936
0
    flb_sds_t tmp;
937
0
    flb_sds_t sign;
938
0
    unsigned char sha256_buf[64] = {0};
939
940
0
    sign = flb_sds_create_size(256);
941
0
    if (!sign) {
942
0
        flb_error("[signv4] cannot create buffer for signature");
943
0
        return NULL;
944
0
    }
945
946
    /* Hashing Algorithm */
947
0
    tmp = flb_sds_cat(sign, "AWS4-HMAC-SHA256\n", 17);
948
0
    if (!tmp) {
949
0
        flb_error("[signv4] cannot add algorithm to signature");
950
0
        flb_sds_destroy(sign);
951
0
        return NULL;
952
0
    }
953
0
    sign = tmp;
954
955
    /* Amazon date */
956
0
    tmp = flb_sds_printf(&sign, "%s\n", amzdate);
957
0
    if (!tmp) {
958
0
        flb_error("[signv4] cannot add amz-date to signature");
959
0
        flb_sds_destroy(sign);
960
0
        return NULL;
961
0
    }
962
0
    sign = tmp;
963
964
    /* Credentials Scope */
965
0
    tmp = flb_sds_printf(&sign, "%s/%s/%s/aws4_request\n",
966
0
                         datestamp, region, service);
967
0
    if (!tmp) {
968
0
        flb_error("[signv4] cannot add credentials scope  to signature");
969
0
        flb_sds_destroy(sign);
970
0
        return NULL;
971
0
    }
972
973
    /* Hash of Canonical Request */
974
0
    result = flb_hash_simple(FLB_HASH_SHA256,
975
0
                             (unsigned char *) cr, flb_sds_len(cr),
976
0
                             sha256_buf, sizeof(sha256_buf));
977
978
0
    if (result != FLB_CRYPTO_SUCCESS) {
979
0
        flb_error("[signv4] error hashing canonical request");
980
0
        flb_sds_destroy(sign);
981
0
        return NULL;
982
0
    }
983
984
0
    for (i = 0; i < 32; i++) {
985
0
        tmp = flb_sds_printf(&sign, "%02x", (unsigned char) sha256_buf[i]);
986
0
        if (!tmp) {
987
0
            flb_error("[signv4] error formatting hashed canonical request");
988
0
            flb_sds_destroy(sign);
989
0
            return NULL;
990
0
        }
991
0
        sign = tmp;
992
0
    }
993
994
0
    return sign;
995
0
}
996
997
/*
998
 * Task 3
999
 * ======
1000
 *
1001
 * https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
1002
 *
1003
 * TODO: The signing key could be cached to improve performance
1004
 */
1005
static flb_sds_t flb_signv4_calculate_signature(flb_sds_t string_to_sign,
1006
                                                char *datestamp, char *service,
1007
                                                char *region, char *secret_key)
1008
0
{
1009
0
    int len;
1010
0
    int klen = 32;
1011
0
    flb_sds_t tmp;
1012
0
    flb_sds_t key;
1013
0
    unsigned char key_date[32];
1014
0
    unsigned char key_region[32];
1015
0
    unsigned char key_service[32];
1016
0
    unsigned char key_signing[32];
1017
0
    unsigned char signature[32];
1018
1019
    /* Compose initial key */
1020
0
    key = flb_sds_create_size(256);
1021
0
    if (!key) {
1022
0
        flb_error("[signv4] cannot create buffer for signature calculation");
1023
0
        return NULL;
1024
0
    }
1025
1026
0
    tmp = flb_sds_printf(&key, "AWS4%s", secret_key);
1027
0
    if (!tmp) {
1028
0
        flb_error("[signv4] error formatting initial key");
1029
0
        flb_sds_destroy(key);
1030
0
        return NULL;
1031
0
    }
1032
0
    key = tmp;
1033
1034
    /* key_date */
1035
0
    len = strlen(datestamp);
1036
0
    hmac_sha256_sign(key_date, (unsigned char *) key, flb_sds_len(key),
1037
0
                     (unsigned char *) datestamp, len);
1038
0
    flb_sds_destroy(key);
1039
1040
    /* key_region */
1041
0
    len = strlen(region);
1042
0
    hmac_sha256_sign(key_region, key_date, klen, (unsigned char *) region, len);
1043
1044
    /* key_service */
1045
0
    len = strlen(service);
1046
0
    hmac_sha256_sign(key_service, key_region, klen, (unsigned char *) service, len);
1047
1048
    /* key_signing */
1049
0
    hmac_sha256_sign(key_signing, key_service, klen,
1050
0
                     (unsigned char *) "aws4_request", 12);
1051
1052
    /* Signature */
1053
0
    hmac_sha256_sign(signature, key_signing, klen,
1054
0
                     (unsigned char *) string_to_sign, flb_sds_len(string_to_sign));
1055
1056
0
    return sha256_to_hex(signature);
1057
0
}
1058
1059
/*
1060
 * Task 4
1061
 * ======
1062
 *
1063
 * https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
1064
 */
1065
static flb_sds_t flb_signv4_add_authorization(struct flb_http_client *c,
1066
                                              char *access_key,
1067
                                              char *datestamp,
1068
                                              char *region, char *service,
1069
                                              flb_sds_t signed_headers,
1070
                                              flb_sds_t signature)
1071
0
{
1072
0
    int ret;
1073
0
    int len;
1074
0
    flb_sds_t tmp;
1075
0
    flb_sds_t header_value;
1076
1077
0
    header_value = flb_sds_create_size(512);
1078
0
    if (!header_value) {
1079
0
        flb_error("[signv4] cannot allocate buffer for authorization header");
1080
0
        return NULL;
1081
0
    }
1082
1083
0
    tmp = flb_sds_printf(&header_value,
1084
0
                         "AWS4-HMAC-SHA256 Credential=%s/%s/%s/%s/aws4_request, "
1085
0
                         "SignedHeaders=%s, Signature=%s",
1086
0
                         access_key, datestamp, region, service,
1087
0
                         signed_headers, signature);
1088
0
    if (!tmp) {
1089
0
        flb_error("[signv4] error composing authorization header");
1090
0
        flb_sds_destroy(header_value);
1091
0
        return NULL;
1092
0
    }
1093
0
    header_value = tmp;
1094
1095
0
    len = flb_sds_len(header_value);
1096
0
    ret = flb_http_add_header(c, "Authorization", 13, header_value, len);
1097
0
    if (ret == -1) {
1098
0
        flb_error("[signv4] could not add authorization header");
1099
0
        flb_sds_destroy(header_value);
1100
0
        return NULL;
1101
1102
0
    }
1103
1104
    /* Return the composed final header for testing if required */
1105
0
    return header_value;
1106
0
}
1107
1108
flb_sds_t flb_signv4_do(struct flb_http_client *c, int normalize_uri,
1109
                        int amz_date_header,
1110
                        time_t t_now,
1111
                        char *region, char *service,
1112
                        int s3_mode,
1113
                        struct flb_aws_provider *provider)
1114
0
{
1115
0
    char amzdate[32];
1116
0
    char datestamp[32];
1117
0
    struct tm *gmt;
1118
0
    flb_sds_t cr;
1119
0
    flb_sds_t string_to_sign;
1120
0
    flb_sds_t signature;
1121
0
    flb_sds_t signed_headers;
1122
0
    flb_sds_t auth_header;
1123
0
    struct flb_aws_credentials *creds;
1124
1125
0
    creds = provider->provider_vtable->get_credentials(provider);
1126
0
    if (!creds) {
1127
0
        flb_error("[signv4] Provider returned no credentials, service=%s",
1128
0
                  service);
1129
0
        return NULL;
1130
0
    }
1131
1132
0
    gmt = flb_malloc(sizeof(struct tm));
1133
0
    if (!gmt) {
1134
0
        flb_errno();
1135
0
        flb_aws_credentials_destroy(creds);
1136
0
        return NULL;
1137
0
    }
1138
1139
0
    if (!gmtime_r(&t_now, gmt)) {
1140
0
        flb_error("[signv4] error converting given unix timestamp");
1141
0
        flb_free(gmt);
1142
0
        flb_aws_credentials_destroy(creds);
1143
0
        return NULL;
1144
0
    }
1145
1146
0
    strftime(amzdate, sizeof(amzdate) - 1, "%Y%m%dT%H%M%SZ", gmt);
1147
0
    strftime(datestamp, sizeof(datestamp) - 1, "%Y%m%d", gmt);
1148
0
    flb_free(gmt);
1149
1150
    /* Task 1: canonical request */
1151
0
    signed_headers = flb_sds_create_size(256);
1152
0
    if (!signed_headers) {
1153
0
        flb_error("[signedv4] cannot allocate buffer for auth signed headers");
1154
0
        flb_aws_credentials_destroy(creds);
1155
0
        return NULL;
1156
0
    }
1157
1158
0
    cr = flb_signv4_canonical_request(c, normalize_uri,
1159
0
                                      amz_date_header, amzdate,
1160
0
                                      creds->session_token, s3_mode,
1161
0
                                      &signed_headers);
1162
0
    if (!cr) {
1163
0
        flb_error("[signv4] failed canonical request");
1164
0
        flb_sds_destroy(signed_headers);
1165
0
        flb_aws_credentials_destroy(creds);
1166
0
        return NULL;
1167
0
    }
1168
1169
    /* Task 2: string to sign */
1170
0
    string_to_sign = flb_signv4_string_to_sign(c, cr, amzdate,
1171
0
                                               datestamp, service, region);
1172
0
    if (!string_to_sign) {
1173
0
        flb_error("[signv4] failed string to sign");
1174
0
        flb_sds_destroy(cr);
1175
0
        flb_sds_destroy(signed_headers);
1176
0
        flb_aws_credentials_destroy(creds);
1177
0
        return NULL;
1178
0
    }
1179
0
    flb_sds_destroy(cr);
1180
1181
    /* Task 3: calculate the signature */
1182
0
    signature = flb_signv4_calculate_signature(string_to_sign, datestamp,
1183
0
                                               service, region,
1184
0
                                               creds->secret_access_key);
1185
0
    if (!signature) {
1186
0
        flb_error("[signv4] failed calculate_string");
1187
0
        flb_sds_destroy(signed_headers);
1188
0
        flb_sds_destroy(string_to_sign);
1189
0
        flb_aws_credentials_destroy(creds);
1190
0
        return NULL;
1191
0
    }
1192
0
    flb_sds_destroy(string_to_sign);
1193
1194
    /* Task 4: add signature to HTTP request */
1195
0
    auth_header = flb_signv4_add_authorization(c,
1196
0
                                               creds->access_key_id,
1197
0
                                               datestamp, region, service,
1198
0
                                               signed_headers, signature);
1199
0
    flb_sds_destroy(signed_headers);
1200
0
    flb_sds_destroy(signature);
1201
0
    flb_aws_credentials_destroy(creds);
1202
1203
0
    if (!auth_header) {
1204
0
        flb_error("[signv4] error creating authorization header");
1205
0
        return NULL;
1206
0
    }
1207
1208
0
    return auth_header;
1209
0
}