1 /* This file is part of Eclat.
2 Copyright (C) 2012-2018 Sergey Poznyakoff.
3
4 Eclat is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 Eclat is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with Eclat. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <string.h>
19 #include <time.h>
20 #include "libeclat.h"
21 #include "sha256.h"
22 #include "grecs.h"
23
24 struct pname {
25 size_t i;
26 char **a;
27 };
28
29 static int
get_param_name(void * sym,void * data)30 get_param_name(void *sym, void *data)
31 {
32 struct grecs_syment *se = sym;
33 struct pname *pn = data;
34 pn->a[pn->i++] = se->name;
35 return 0;
36 }
37
38 static int
compnames(const void * a,const void * b)39 compnames(const void *a, const void *b)
40 {
41 char * const *ac = a;
42 char * const *bc = b;
43 return strcmp(*ac, *bc);
44 }
45
46 static void
requestsign2(struct ec2_request * req,char * secret)47 requestsign2(struct ec2_request *req, char *secret)
48 {
49 char **pnames;
50 size_t i, n;
51 struct grecs_txtacc *acc;
52 struct pname pn;
53 char *str;
54 char digest[SHA256_DIGEST_SIZE];
55 char *signature;
56 size_t siglen;
57 const char *verb;
58 char tsbuf[22];
59 time_t t;
60
61 acc = grecs_txtacc_create();
62
63 /* Add default parameters */
64 eclat_request_add_param(req, "AWSAccessKeyId", req->access_key);
65 eclat_request_add_param(req, "SignatureMethod", "HmacSHA256");
66 eclat_request_add_param(req, "SignatureVersion", "2");
67 if (req->token)
68 eclat_request_add_param(req, "SecurityToken", req->token);
69
70 time(&t);
71 strftime(tsbuf, sizeof(tsbuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t));
72 eclat_request_add_param(req, "Timestamp", tsbuf);
73
74 eclat_request_encode(req);
75
76 /* Collect and sort parameter names */
77 n = grecs_symtab_count(req->params);
78 pnames = grecs_calloc(n, sizeof(pnames[0]));
79 pn.i = 0;
80 pn.a = pnames;
81 grecs_symtab_foreach(req->params, get_param_name, &pn);
82 qsort(pnames, n, sizeof(pnames[0]), compnames);
83
84 verb = (req->flags & EC2_RF_POST) ? "POST" : "GET";
85 grecs_txtacc_grow_string(acc, verb);
86 grecs_txtacc_grow_char(acc, '\n');
87 grecs_txtacc_grow_string(acc, req->endpoint);
88 grecs_txtacc_grow_char(acc, '\n');
89 grecs_txtacc_grow_string(acc, req->uri);
90 grecs_txtacc_grow_char(acc, '\n');
91
92 /* Append a canonicalized request string */
93 for (i = 0; i < n; i++) {
94 struct ec2_param *p, key;
95
96 key.name = pnames[i];
97 p = grecs_symtab_lookup_or_install(req->params, &key, NULL);
98 if (!p)
99 abort();
100 if (i != 0)
101 grecs_txtacc_grow_char(acc, '&');
102 grecs_txtacc_grow_string(acc, p->name);
103 if (p->value) {
104 grecs_txtacc_grow_char(acc, '=');
105 grecs_txtacc_grow_string(acc, p->value);
106 }
107 }
108 grecs_txtacc_grow_char(acc, 0);
109 str = grecs_txtacc_finish(acc, 0);
110
111 hmac_sha256(str, strlen(str), secret, strlen(secret), digest);
112
113 eclat_base64_encode((unsigned char *)digest, sizeof(digest),
114 (unsigned char**) &signature, &siglen);
115 eclat_request_add_param_encoded(req, "Signature", signature);
116 free(signature);
117
118 grecs_txtacc_free(acc);
119 free(pnames);
120
121 /*FIXME
122 t += req->ttl;
123 strftime(tsbuf, sizeof(tsbuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t));
124 eclat_request_add_param(req, "Expires", tsbuf);
125 */
126 }
127
128 void
eclat_hex_encode(unsigned char * input,size_t inlen,char ** poutput,size_t * poutlen)129 eclat_hex_encode(unsigned char *input, size_t inlen,
130 char **poutput, size_t *poutlen)
131 {
132 size_t l = inlen * 2;
133 char *p = grecs_malloc(l + 1);
134
135 *poutput = p;
136 *poutlen = l;
137
138 while (inlen--) {
139 static char xdig[] = "0123456789abcdef";
140 unsigned c = *input++;
141
142 *p++ = xdig[c >> 4];
143 *p++ = xdig[c & 0xf];
144 }
145 *p = 0;
146 }
147
148 /* Ref. http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
149 */
150 static void
requestsign4(struct ec2_request * req,char * secret)151 requestsign4(struct ec2_request *req, char *secret)
152 {
153 char **pnames;
154 struct pname pn;
155 size_t i, n;
156 struct grecs_txtacc *acc;
157 char digest[SHA256_DIGEST_SIZE];
158 const char *verb;
159 char tsbuf[22];
160 time_t t;
161 char *p;
162 char const *payload;
163 char *hashstr = NULL;
164 size_t hashsize = 0;
165 char *string_to_sign;
166 static char algostr[] = "AWS4-HMAC-SHA256";
167 static char termstr[] = "aws4_request";
168 char *canonical_req;
169 static char *signed_headers = "host;x-amz-date";
170 char *credential;
171 char *signature;
172 struct sha256_ctx ctx;
173 char *service;
174 size_t service_len;
175
176 service = req->endpoint;
177 service_len = strcspn(service, ".");
178
179 /* Create text accumulator */
180 acc = grecs_txtacc_create();
181
182 /* Timestamp */
183 time(&t);
184 strftime(tsbuf, sizeof(tsbuf), "%Y%m%dT%H%M%SZ", gmtime(&t));
185
186 /* Build credential */
187 grecs_txtacc_grow_string(acc, req->access_key);
188 grecs_txtacc_grow_char(acc, '/');
189 grecs_txtacc_grow(acc, tsbuf, 8); /* %Y%m%d part only */
190 grecs_txtacc_grow_char(acc, '/');
191 grecs_txtacc_grow_string(acc, req->region);
192 grecs_txtacc_grow_char(acc, '/');
193 grecs_txtacc_grow(acc, service, service_len);
194 grecs_txtacc_grow_char(acc, '/');
195 grecs_txtacc_grow_string(acc, termstr);
196 grecs_txtacc_grow_char(acc, 0);
197 credential = grecs_txtacc_finish(acc, 0);
198
199 eclat_request_add_header(req, "Host", req->endpoint);
200 eclat_request_add_header(req, "X-Amz-Date", tsbuf);
201
202 /* Encode the request */
203 eclat_request_encode(req);
204
205 /* Collect and sort parameter names */
206 n = grecs_symtab_count(req->params);
207 pnames = grecs_calloc(n, sizeof(pnames[0]));
208 pn.i = 0;
209 pn.a = pnames;
210 grecs_symtab_foreach(req->params, get_param_name, &pn);
211 qsort(pnames, n, sizeof(pnames[0]), compnames);
212
213 /* Create a canonical request */
214 verb = (req->flags & EC2_RF_POST) ? "POST" : "GET";
215 grecs_txtacc_grow_string(acc, verb);
216 grecs_txtacc_grow_char(acc, '\n');
217 grecs_txtacc_grow_string(acc, req->uri);
218 grecs_txtacc_grow_char(acc, '\n');
219 /* Append canonicalized request string */
220 for (i = 0; i < n; i++) {
221 struct ec2_param *p, key;
222
223 key.name = pnames[i];
224 p = grecs_symtab_lookup_or_install(req->params, &key, NULL);
225 if (!p)
226 abort();
227 if (i != 0)
228 grecs_txtacc_grow_char(acc, '&');
229 grecs_txtacc_grow_string(acc, p->name);
230 if (p->value) {
231 grecs_txtacc_grow_char(acc, '=');
232 grecs_txtacc_grow_string(acc, p->value);
233 }
234 }
235 grecs_txtacc_grow_char(acc, '\n');
236
237 /* CanonicalHeaders */
238 grecs_txtacc_grow_string(acc, "host");
239 grecs_txtacc_grow_char(acc, ':');
240 grecs_txtacc_grow_string(acc, req->endpoint);
241 grecs_txtacc_grow_char(acc, '\n');
242
243 grecs_txtacc_grow_string(acc, "x-amz-date");
244 grecs_txtacc_grow_char(acc, ':');
245 grecs_txtacc_grow_string(acc, tsbuf);
246 grecs_txtacc_grow_char(acc, '\n');
247
248 /* end of headers */
249 grecs_txtacc_grow_char(acc, '\n');
250 /* Signed Headers */
251 grecs_txtacc_grow_string(acc, signed_headers);
252 grecs_txtacc_grow_char(acc, '\n');
253 /* Payload hash */
254 if (req->flags & EC2_RF_POST) {
255 payload = req->postdata;
256 } else
257 payload = "";
258
259 sha256_init_ctx(&ctx);
260 sha256_process_bytes(payload, strlen(payload), &ctx);
261 sha256_finish_ctx(&ctx, digest);
262
263 eclat_hex_encode((unsigned char *)digest, sizeof(digest),
264 &hashstr, &hashsize);
265 grecs_txtacc_grow(acc, hashstr, hashsize);
266 free(hashstr);
267
268 grecs_txtacc_grow_char(acc, 0);
269 canonical_req = grecs_txtacc_finish(acc, 0);
270
271 sha256_init_ctx(&ctx);
272 sha256_process_bytes(canonical_req, strlen(canonical_req), &ctx);
273 sha256_finish_ctx(&ctx, digest);
274 eclat_hex_encode((unsigned char *)digest, sizeof(digest),
275 &canonical_req, &hashsize);
276
277 /* Create a string to sign */
278 grecs_txtacc_grow_string(acc, algostr);
279 grecs_txtacc_grow_char(acc, '\n');
280 grecs_txtacc_grow_string(acc, tsbuf);
281 grecs_txtacc_grow_char(acc, '\n');
282 /* credential scope: */
283 grecs_txtacc_grow(acc, tsbuf, 8); /* %Y%m%d part only */
284 grecs_txtacc_grow_char(acc, '/');
285 grecs_txtacc_grow_string(acc, req->region);
286 grecs_txtacc_grow_char(acc, '/');
287 grecs_txtacc_grow(acc, service, service_len);
288 grecs_txtacc_grow_char(acc, '/');
289 grecs_txtacc_grow_string(acc, termstr);
290 grecs_txtacc_grow_char(acc, '\n');
291
292 /* hashed request */
293 grecs_txtacc_grow_string(acc, canonical_req);
294
295 grecs_txtacc_grow_char(acc, 0);
296 string_to_sign = grecs_txtacc_finish(acc, 0);
297
298 /* Derive a signing key */
299 grecs_txtacc_grow_string(acc, "AWS4");
300 grecs_txtacc_grow_string(acc, secret);
301 grecs_txtacc_grow_char(acc, 0);
302 p = grecs_txtacc_finish(acc, 0);
303
304 hmac_sha256(tsbuf, 8, p, strlen(p), digest);
305 hmac_sha256(req->region, strlen(req->region), digest, sizeof(digest),
306 digest);
307 hmac_sha256(service, service_len, digest, sizeof(digest),
308 digest);
309 hmac_sha256(termstr, strlen(termstr), digest, sizeof(digest),
310 digest);
311
312 /* Calculate the signature */
313 hmac_sha256(string_to_sign, strlen(string_to_sign),
314 digest, sizeof(digest),
315 digest);
316 eclat_hex_encode((unsigned char *)digest, sizeof(digest),
317 &signature, &hashsize);
318
319 if (req->token)
320 eclat_request_add_header(req, "X-Amz-Security-Token", req->token);
321
322 /* Build authorization header */
323 grecs_txtacc_grow_string(acc, algostr);
324 grecs_txtacc_grow_string(acc, " Credential=");
325 grecs_txtacc_grow_string(acc, credential);
326 grecs_txtacc_grow_string(acc, ", SignedHeaders=");
327 grecs_txtacc_grow_string(acc, signed_headers);
328 grecs_txtacc_grow_string(acc, ", Signature=");
329 grecs_txtacc_grow_string(acc, signature);
330 grecs_txtacc_grow_char(acc, 0);
331 p = grecs_txtacc_finish(acc, 0);
332 eclat_request_add_header(req, "Authorization", p);
333
334 free(signature);
335 grecs_txtacc_free(acc);
336 /* Encode the request */
337 eclat_request_encode(req);
338 }
339
340
341 struct qsimpl {
342 char *qs_version;
343 void (*qs_fun)(struct ec2_request *, char *);
344 };
345
346 static struct qsimpl qstab[] = {
347 { "2", requestsign2 },
348 { "4", requestsign4 },
349 { NULL }
350 };
351
352 void
eclat_request_sign(struct ec2_request * req,char * secret,char * version)353 eclat_request_sign(struct ec2_request *req, char *secret, char *version)
354 {
355 struct qsimpl *qs;
356
357 for (qs = qstab; qs->qs_version && strcmp(qs->qs_version, version);
358 qs++)
359 ;
360
361 if (qs->qs_version)
362 qs->qs_fun(req, secret);
363 else {
364 err("INTERNAL ERROR: unsupported version %s", version);
365 abort();
366 }
367 }
368