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