Imported Upstream version 3.25.0
[platform/upstream/cmake.git] / Utilities / cmcurl / lib / http_aws_sigv4.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
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
36 #include "strcase.h"
37 #include "parsedate.h"
38 #include "sendf.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     ret = Curl_hmacit(Curl_HMAC_SHA256,     \
52                       (unsigned char *)k,   \
53                       (unsigned int)kl,     \
54                       (unsigned char *)d,   \
55                       (unsigned int)dl, o); \
56     if(ret) {                               \
57       goto fail;                            \
58     }                                       \
59   } while(0)
60
61 #define TIMESTAMP_SIZE 17
62
63 static void sha256_to_hex(char *dst, unsigned char *sha, size_t dst_l)
64 {
65   int i;
66
67   DEBUGASSERT(dst_l >= 65);
68   for(i = 0; i < 32; ++i) {
69     msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]);
70   }
71 }
72
73 static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr)
74 {
75   char *tmp = Curl_checkheaders(data, sig_hdr, strlen(sig_hdr));
76
77   if(tmp)
78     return tmp;
79   return Curl_checkheaders(data, STRCONST("Date"));
80 }
81
82 /* remove whitespace, and lowercase all headers */
83 static void trim_headers(struct curl_slist *head)
84 {
85   struct curl_slist *l;
86   for(l = head; l; l = l->next) {
87     char *value; /* to read from */
88     char *store;
89     size_t colon = strcspn(l->data, ":");
90     Curl_strntolower(l->data, l->data, colon);
91
92     value = &l->data[colon];
93     if(!*value)
94       continue;
95     ++value;
96     store = value;
97
98     /* skip leading whitespace */
99     while(*value && ISBLANK(*value))
100       value++;
101
102     while(*value) {
103       int space = 0;
104       while(*value && ISBLANK(*value)) {
105         value++;
106         space++;
107       }
108       if(space) {
109         /* replace any number of consecutive whitespace with a single space,
110            unless at the end of the string, then nothing */
111         if(*value)
112           *store++ = ' ';
113       }
114       else
115         *store++ = *value++;
116     }
117     *store = 0; /* null terminate */
118   }
119 }
120
121 /* maximum lenth for the aws sivg4 parts */
122 #define MAX_SIGV4_LEN 64
123 #define MAX_SIGV4_LEN_TXT "64"
124
125 #define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date"))
126
127 #define MAX_HOST_LEN 255
128 /* FQDN + host: */
129 #define FULL_HOST_LEN (MAX_HOST_LEN + sizeof("host:"))
130
131 /* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */
132 #define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1)
133
134 /* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */
135 static CURLcode make_headers(struct Curl_easy *data,
136                              const char *hostname,
137                              char *timestamp,
138                              char *provider1,
139                              char **date_header,
140                              struct dynbuf *canonical_headers,
141                              struct dynbuf *signed_headers)
142 {
143   char date_hdr_key[DATE_HDR_KEY_LEN];
144   char date_full_hdr[DATE_FULL_HDR_LEN];
145   struct curl_slist *head = NULL;
146   struct curl_slist *tmp_head = NULL;
147   CURLcode ret = CURLE_OUT_OF_MEMORY;
148   struct curl_slist *l;
149   int again = 1;
150
151   /* provider1 mid */
152   Curl_strntolower(provider1, provider1, strlen(provider1));
153   provider1[0] = Curl_raw_toupper(provider1[0]);
154
155   msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1);
156
157   /* provider1 lowercase */
158   Curl_strntolower(provider1, provider1, 1); /* first byte only */
159   msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
160             "x-%s-date:%s", provider1, timestamp);
161
162   if(Curl_checkheaders(data, STRCONST("Host"))) {
163     head = NULL;
164   }
165   else {
166     char full_host[FULL_HOST_LEN + 1];
167
168     if(data->state.aptr.host) {
169       size_t pos;
170
171       if(strlen(data->state.aptr.host) > FULL_HOST_LEN) {
172         ret = CURLE_URL_MALFORMAT;
173         goto fail;
174       }
175       strcpy(full_host, data->state.aptr.host);
176       /* remove /r/n as the separator for canonical request must be '\n' */
177       pos = strcspn(full_host, "\n\r");
178       full_host[pos] = 0;
179     }
180     else {
181       if(strlen(hostname) > MAX_HOST_LEN) {
182         ret = CURLE_URL_MALFORMAT;
183         goto fail;
184       }
185       msnprintf(full_host, FULL_HOST_LEN, "host:%s", hostname);
186     }
187
188     head = curl_slist_append(NULL, full_host);
189     if(!head)
190       goto fail;
191   }
192
193
194   for(l = data->set.headers; l; l = l->next) {
195     tmp_head = curl_slist_append(head, l->data);
196     if(!tmp_head)
197       goto fail;
198     head = tmp_head;
199   }
200
201   trim_headers(head);
202
203   *date_header = find_date_hdr(data, date_hdr_key);
204   if(!*date_header) {
205     tmp_head = curl_slist_append(head, date_full_hdr);
206     if(!tmp_head)
207       goto fail;
208     head = tmp_head;
209     *date_header = curl_maprintf("%s: %s", date_hdr_key, timestamp);
210   }
211   else {
212     char *value;
213
214     *date_header = strdup(*date_header);
215     if(!*date_header)
216       goto fail;
217
218     value = strchr(*date_header, ':');
219     if(!value)
220       goto fail;
221     ++value;
222     while(ISBLANK(*value))
223       ++value;
224     strncpy(timestamp, value, TIMESTAMP_SIZE - 1);
225     timestamp[TIMESTAMP_SIZE - 1] = 0;
226   }
227
228   /* alpha-sort in a case sensitive manner */
229   do {
230     again = 0;
231     for(l = head; l; l = l->next) {
232       struct curl_slist *next = l->next;
233
234       if(next && strcmp(l->data, next->data) > 0) {
235         char *tmp = l->data;
236
237         l->data = next->data;
238         next->data = tmp;
239         again = 1;
240       }
241     }
242   } while(again);
243
244   for(l = head; l; l = l->next) {
245     char *tmp;
246
247     if(Curl_dyn_add(canonical_headers, l->data))
248       goto fail;
249     if(Curl_dyn_add(canonical_headers, "\n"))
250       goto fail;
251
252     tmp = strchr(l->data, ':');
253     if(tmp)
254       *tmp = 0;
255
256     if(l != head) {
257       if(Curl_dyn_add(signed_headers, ";"))
258         goto fail;
259     }
260     if(Curl_dyn_add(signed_headers, l->data))
261       goto fail;
262   }
263
264   ret = CURLE_OK;
265 fail:
266   curl_slist_free_all(head);
267
268   return ret;
269 }
270
271 CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
272 {
273   CURLcode ret = CURLE_OUT_OF_MEMORY;
274   struct connectdata *conn = data->conn;
275   size_t len;
276   const char *arg;
277   char provider0[MAX_SIGV4_LEN + 1]="";
278   char provider1[MAX_SIGV4_LEN + 1]="";
279   char region[MAX_SIGV4_LEN + 1]="";
280   char service[MAX_SIGV4_LEN + 1]="";
281   const char *hostname = conn->host.name;
282   time_t clock;
283   struct tm tm;
284   char timestamp[TIMESTAMP_SIZE];
285   char date[9];
286   struct dynbuf canonical_headers;
287   struct dynbuf signed_headers;
288   char *date_header = NULL;
289   const char *post_data = data->set.postfields;
290   size_t post_data_len = 0;
291   unsigned char sha_hash[32];
292   char sha_hex[65];
293   char *canonical_request = NULL;
294   char *request_type = NULL;
295   char *credential_scope = NULL;
296   char *str_to_sign = NULL;
297   const char *user = data->state.aptr.user ? data->state.aptr.user : "";
298   char *secret = NULL;
299   unsigned char sign0[32] = {0};
300   unsigned char sign1[32] = {0};
301   char *auth_headers = NULL;
302
303   DEBUGASSERT(!proxy);
304   (void)proxy;
305
306   if(Curl_checkheaders(data, STRCONST("Authorization"))) {
307     /* Authorization already present, Bailing out */
308     return CURLE_OK;
309   }
310
311   /* we init thoses buffers here, so goto fail will free initialized dynbuf */
312   Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
313   Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
314
315   /*
316    * Parameters parsing
317    * Google and Outscale use the same OSC or GOOG,
318    * but Amazon uses AWS and AMZ for header arguments.
319    * AWS is the default because most of non-amazon providers
320    * are still using aws:amz as a prefix.
321    */
322   arg = data->set.str[STRING_AWS_SIGV4] ?
323     data->set.str[STRING_AWS_SIGV4] : "aws:amz";
324
325   /* provider1[:provider2[:region[:service]]]
326
327      No string can be longer than N bytes of non-whitespace
328    */
329   (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]"
330                ":%" MAX_SIGV4_LEN_TXT "[^:]"
331                ":%" MAX_SIGV4_LEN_TXT "[^:]"
332                ":%" MAX_SIGV4_LEN_TXT "s",
333                provider0, provider1, region, service);
334   if(!provider0[0]) {
335     failf(data, "first provider can't be empty");
336     ret = CURLE_BAD_FUNCTION_ARGUMENT;
337     goto fail;
338   }
339   else if(!provider1[0])
340     strcpy(provider1, provider0);
341
342   if(!service[0]) {
343     char *hostdot = strchr(hostname, '.');
344     if(!hostdot) {
345       failf(data, "service missing in parameters and hostname");
346       ret = CURLE_URL_MALFORMAT;
347       goto fail;
348     }
349     len = hostdot - hostname;
350     if(len > MAX_SIGV4_LEN) {
351       failf(data, "service too long in hostname");
352       ret = CURLE_URL_MALFORMAT;
353       goto fail;
354     }
355     strncpy(service, hostname, len);
356     service[len] = '\0';
357
358     if(!region[0]) {
359       const char *reg = hostdot + 1;
360       const char *hostreg = strchr(reg, '.');
361       if(!hostreg) {
362         failf(data, "region missing in parameters and hostname");
363         ret = CURLE_URL_MALFORMAT;
364         goto fail;
365       }
366       len = hostreg - reg;
367       if(len > MAX_SIGV4_LEN) {
368         failf(data, "region too long in hostname");
369         ret = CURLE_URL_MALFORMAT;
370         goto fail;
371       }
372       strncpy(region, reg, len);
373       region[len] = '\0';
374     }
375   }
376
377 #ifdef DEBUGBUILD
378   {
379     char *force_timestamp = getenv("CURL_FORCETIME");
380     if(force_timestamp)
381       clock = 0;
382     else
383       time(&clock);
384   }
385 #else
386   time(&clock);
387 #endif
388   ret = Curl_gmtime(clock, &tm);
389   if(ret) {
390     goto fail;
391   }
392   if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
393     ret = CURLE_OUT_OF_MEMORY;
394     goto fail;
395   }
396
397   ret = make_headers(data, hostname, timestamp, provider1,
398                      &date_header, &canonical_headers, &signed_headers);
399   if(ret)
400     goto fail;
401   ret = CURLE_OUT_OF_MEMORY;
402
403   memcpy(date, timestamp, sizeof(date));
404   date[sizeof(date) - 1] = 0;
405
406   if(post_data) {
407     if(data->set.postfieldsize < 0)
408       post_data_len = strlen(post_data);
409     else
410       post_data_len = (size_t)data->set.postfieldsize;
411   }
412   if(Curl_sha256it(sha_hash, (const unsigned char *) post_data,
413                    post_data_len))
414     goto fail;
415
416   sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
417
418   {
419     Curl_HttpReq httpreq;
420     const char *method;
421
422     Curl_http_method(data, conn, &method, &httpreq);
423
424     canonical_request =
425       curl_maprintf("%s\n" /* HTTPRequestMethod */
426                     "%s\n" /* CanonicalURI */
427                     "%s\n" /* CanonicalQueryString */
428                     "%s\n" /* CanonicalHeaders */
429                     "%s\n" /* SignedHeaders */
430                     "%s",  /* HashedRequestPayload in hex */
431                     method,
432                     data->state.up.path,
433                     data->state.up.query ? data->state.up.query : "",
434                     Curl_dyn_ptr(&canonical_headers),
435                     Curl_dyn_ptr(&signed_headers),
436                     sha_hex);
437     if(!canonical_request)
438       goto fail;
439   }
440
441   /* provider 0 lowercase */
442   Curl_strntolower(provider0, provider0, strlen(provider0));
443   request_type = curl_maprintf("%s4_request", provider0);
444   if(!request_type)
445     goto fail;
446
447   credential_scope = curl_maprintf("%s/%s/%s/%s",
448                                    date, region, service, request_type);
449   if(!credential_scope)
450     goto fail;
451
452   if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request,
453                    strlen(canonical_request)))
454     goto fail;
455
456   sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
457
458   /* provider 0 uppercase */
459   Curl_strntoupper(provider0, provider0, strlen(provider0));
460
461   /*
462    * Google allows using RSA key instead of HMAC, so this code might change
463    * in the future. For now we ony support HMAC.
464    */
465   str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */
466                               "%s\n" /* RequestDateTime */
467                               "%s\n" /* CredentialScope */
468                               "%s",  /* HashedCanonicalRequest in hex */
469                               provider0,
470                               timestamp,
471                               credential_scope,
472                               sha_hex);
473   if(!str_to_sign) {
474     goto fail;
475   }
476
477   /* provider 0 uppercase */
478   secret = curl_maprintf("%s4%s", provider0,
479                          data->state.aptr.passwd ?
480                          data->state.aptr.passwd : "");
481   if(!secret)
482     goto fail;
483
484   HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
485   HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1);
486   HMAC_SHA256(sign1, sizeof(sign1), service, strlen(service), sign0);
487   HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
488   HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
489
490   sha256_to_hex(sha_hex, sign0, sizeof(sha_hex));
491
492   /* provider 0 uppercase */
493   auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 "
494                                "Credential=%s/%s, "
495                                "SignedHeaders=%s, "
496                                "Signature=%s\r\n"
497                                "%s\r\n",
498                                provider0,
499                                user,
500                                credential_scope,
501                                Curl_dyn_ptr(&signed_headers),
502                                sha_hex,
503                                date_header);
504   if(!auth_headers) {
505     goto fail;
506   }
507
508   Curl_safefree(data->state.aptr.userpwd);
509   data->state.aptr.userpwd = auth_headers;
510   data->state.authhost.done = TRUE;
511   ret = CURLE_OK;
512
513 fail:
514   Curl_dyn_free(&canonical_headers);
515   Curl_dyn_free(&signed_headers);
516   free(canonical_request);
517   free(request_type);
518   free(credential_scope);
519   free(str_to_sign);
520   free(secret);
521   free(date_header);
522   return ret;
523 }
524
525 #endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */