]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_auth_s3.cc
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / rgw / rgw_auth_s3.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include <map>
5 #include <string>
6
7 #include "common/armor.h"
8 #include "common/utf8.h"
9 #include "rgw_common.h"
10 #include "rgw_client_io.h"
11 #include "rgw_rest.h"
12 #include "rgw_crypt_sanitize.h"
13 #define dout_context g_ceph_context
14 #define dout_subsys ceph_subsys_rgw
15
16 static const auto signed_subresources = {
17 "acl",
18 "cors",
19 "delete",
20 "lifecycle",
21 "location",
22 "logging",
23 "notification",
24 "partNumber",
25 "policy",
26 "requestPayment",
27 "response-cache-control",
28 "response-content-disposition",
29 "response-content-encoding",
30 "response-content-language",
31 "response-content-type",
32 "response-expires",
33 "torrent",
34 "uploadId",
35 "uploads",
36 "start-date",
37 "end-date",
38 "versionId",
39 "versioning",
40 "versions",
41 "website"
42 };
43
44 /*
45 * ?get the canonical amazon-style header for something?
46 */
47
48 static std::string
49 get_canon_amz_hdr(const std::map<std::string, std::string>& meta_map)
50 {
51 std::string dest;
52
53 for (const auto& kv : meta_map) {
54 dest.append(kv.first);
55 dest.append(":");
56 dest.append(kv.second);
57 dest.append("\n");
58 }
59
60 return dest;
61 }
62
63 /*
64 * ?get the canonical representation of the object's location
65 */
66 static std::string
67 get_canon_resource(const char* const request_uri,
68 const std::map<std::string, std::string>& sub_resources)
69 {
70 std::string dest;
71
72 if (request_uri) {
73 dest.append(request_uri);
74 }
75
76 bool initial = true;
77 for (const auto& subresource : signed_subresources) {
78 const auto iter = sub_resources.find(subresource);
79 if (iter == std::end(sub_resources)) {
80 continue;
81 }
82
83 if (initial) {
84 dest.append("?");
85 initial = false;
86 } else {
87 dest.append("&");
88 }
89
90 dest.append(iter->first);
91 if (! iter->second.empty()) {
92 dest.append("=");
93 dest.append(iter->second);
94 }
95 }
96
97 dout(10) << "get_canon_resource(): dest=" << dest << dendl;
98 return dest;
99 }
100
101 /*
102 * get the header authentication information required to
103 * compute a request's signature
104 */
105 void rgw_create_s3_canonical_header(
106 const char* const method,
107 const char* const content_md5,
108 const char* const content_type,
109 const char* const date,
110 const std::map<std::string, std::string>& meta_map,
111 const char* const request_uri,
112 const std::map<std::string, std::string>& sub_resources,
113 std::string& dest_str)
114 {
115 std::string dest;
116
117 if (method) {
118 dest = method;
119 }
120 dest.append("\n");
121
122 if (content_md5) {
123 dest.append(content_md5);
124 }
125 dest.append("\n");
126
127 if (content_type) {
128 dest.append(content_type);
129 }
130 dest.append("\n");
131
132 if (date) {
133 dest.append(date);
134 }
135 dest.append("\n");
136
137 dest.append(get_canon_amz_hdr(meta_map));
138 dest.append(get_canon_resource(request_uri, sub_resources));
139
140 dest_str = dest;
141 }
142
143 int rgw_get_s3_header_digest(const string& auth_hdr, const string& key, string& dest)
144 {
145 if (key.empty())
146 return -EINVAL;
147
148 char hmac_sha1[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE];
149 calc_hmac_sha1(key.c_str(), key.size(), auth_hdr.c_str(), auth_hdr.size(), hmac_sha1);
150
151 char b64[64]; /* 64 is really enough */
152 int ret = ceph_armor(b64, b64 + 64, hmac_sha1,
153 hmac_sha1 + CEPH_CRYPTO_HMACSHA1_DIGESTSIZE);
154 if (ret < 0) {
155 dout(10) << "ceph_armor failed" << dendl;
156 return ret;
157 }
158 b64[ret] = '\0';
159
160 dest = b64;
161
162 return 0;
163 }
164
165 void rgw_hash_s3_string_sha256(const char *data, int len, string& dest)
166 {
167 calc_hash_sha256(data, len, dest);
168 }
169
170 static inline bool is_base64_for_content_md5(unsigned char c) {
171 return (isalnum(c) || isspace(c) || (c == '+') || (c == '/') || (c == '='));
172 }
173
174 /*
175 * get the header authentication information required to
176 * compute a request's signature
177 */
178 bool rgw_create_s3_canonical_header(const req_info& info,
179 utime_t* const header_time,
180 std::string& dest,
181 const bool qsr)
182 {
183 const char* const content_md5 = info.env->get("HTTP_CONTENT_MD5");
184 if (content_md5) {
185 for (const char *p = content_md5; *p; p++) {
186 if (!is_base64_for_content_md5(*p)) {
187 dout(0) << "NOTICE: bad content-md5 provided (not base64),"
188 << " aborting request p=" << *p << " " << (int)*p << dendl;
189 return false;
190 }
191 }
192 }
193
194 const char *content_type = info.env->get("CONTENT_TYPE");
195
196 std::string date;
197 if (qsr) {
198 date = info.args.get("Expires");
199 } else {
200 const char *str = info.env->get("HTTP_DATE");
201 const char *req_date = str;
202 if (str) {
203 date = str;
204 } else {
205 req_date = info.env->get("HTTP_X_AMZ_DATE");
206 if (!req_date) {
207 dout(0) << "NOTICE: missing date for auth header" << dendl;
208 return false;
209 }
210 }
211
212 if (header_time) {
213 struct tm t;
214 if (!parse_rfc2616(req_date, &t)) {
215 dout(0) << "NOTICE: failed to parse date for auth header" << dendl;
216 return false;
217 }
218 if (t.tm_year < 70) {
219 dout(0) << "NOTICE: bad date (predates epoch): " << req_date << dendl;
220 return false;
221 }
222 *header_time = utime_t(internal_timegm(&t), 0);
223 }
224 }
225
226 const auto& meta_map = info.x_meta_map;
227 const auto& sub_resources = info.args.get_sub_resources();
228
229 std::string request_uri;
230 if (info.effective_uri.empty()) {
231 request_uri = info.request_uri;
232 } else {
233 request_uri = info.effective_uri;
234 }
235
236 rgw_create_s3_canonical_header(info.method, content_md5, content_type,
237 date.c_str(), meta_map, request_uri.c_str(),
238 sub_resources, dest);
239 return true;
240 }
241
242 /*
243 * assemble canonical request for signature version 4
244 */
245 void rgw_assemble_s3_v4_canonical_request(const char *method, const char *canonical_uri, const char *canonical_qs,
246 const char *canonical_hdrs, const char *signed_hdrs, const char *request_payload_hash,
247 string& dest_str)
248 {
249 string dest;
250
251 if (method)
252 dest = method;
253 dest.append("\n");
254
255 if (canonical_uri) {
256 dest.append(canonical_uri);
257 }
258 dest.append("\n");
259
260 if (canonical_qs) {
261 dest.append(canonical_qs);
262 }
263 dest.append("\n");
264
265 if (canonical_hdrs)
266 dest.append(canonical_hdrs);
267 dest.append("\n");
268
269 if (signed_hdrs)
270 dest.append(signed_hdrs);
271 dest.append("\n");
272
273 if (request_payload_hash)
274 dest.append(request_payload_hash);
275
276 dest_str = dest;
277 }
278
279 /*
280 * create canonical request for signature version 4
281 */
282 void rgw_create_s3_v4_canonical_request(struct req_state *s, const string& canonical_uri, const string& canonical_qs,
283 const string& canonical_hdrs, const string& signed_hdrs, const string& request_payload,
284 bool unsigned_payload, string& canonical_req, string& canonical_req_hash)
285 {
286 string request_payload_hash;
287
288 if (unsigned_payload) {
289 request_payload_hash = "UNSIGNED-PAYLOAD";
290 } else {
291 if (s->aws4_auth_needs_complete) {
292 request_payload_hash = AWS_AUTHv4_IO(s)->grab_aws4_sha256_hash();
293 } else {
294 if (s->aws4_auth_streaming_mode) {
295 request_payload_hash = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD";
296 } else {
297 rgw_hash_s3_string_sha256(request_payload.c_str(), request_payload.size(), request_payload_hash);
298 }
299 }
300 }
301
302 s->aws4_auth->payload_hash = request_payload_hash;
303
304 ldout(s->cct, 10) << "payload request hash = " << request_payload_hash << dendl;
305
306 rgw_assemble_s3_v4_canonical_request(s->info.method, canonical_uri.c_str(),
307 canonical_qs.c_str(), canonical_hdrs.c_str(), signed_hdrs.c_str(),
308 request_payload_hash.c_str(), canonical_req);
309
310 rgw_hash_s3_string_sha256(canonical_req.c_str(), canonical_req.size(), canonical_req_hash);
311
312 ldout(s->cct, 10) << "canonical request = " << canonical_req << dendl;
313 ldout(s->cct, 10) << "canonical request hash = " << canonical_req_hash << dendl;
314 }
315
316 /*
317 * assemble string to sign for signature version 4
318 */
319 void rgw_assemble_s3_v4_string_to_sign(const char *algorithm, const char *request_date,
320 const char *credential_scope, const char *hashed_qr, string& dest_str)
321 {
322 string dest;
323
324 if (algorithm)
325 dest = algorithm;
326 dest.append("\n");
327
328 if (request_date)
329 dest.append(request_date);
330 dest.append("\n");
331
332 if (credential_scope)
333 dest.append(credential_scope);
334 dest.append("\n");
335
336 if (hashed_qr)
337 dest.append(hashed_qr);
338
339 dest_str = dest;
340 }
341
342 /*
343 * create string to sign for signature version 4
344 */
345 void rgw_create_s3_v4_string_to_sign(CephContext *cct, const string& algorithm, const string& request_date,
346 const string& credential_scope, const string& hashed_qr,
347 string& string_to_sign) {
348
349 rgw_assemble_s3_v4_string_to_sign(algorithm.c_str(), request_date.c_str(),
350 credential_scope.c_str(), hashed_qr.c_str(), string_to_sign);
351
352 ldout(cct, 10) << "string to sign = " << rgw::crypt_sanitize::log_content{string_to_sign.c_str()} << dendl;
353 }
354
355 /*
356 * calculate the AWS signature version 4
357 */
358 int rgw_calculate_s3_v4_aws_signature(struct req_state *s,
359 const string& access_key_id, const string &date, const string& region,
360 const string& service, const string& string_to_sign, string& signature) {
361
362 map<string, RGWAccessKey>::iterator iter = s->user->access_keys.find(access_key_id);
363 if (iter == s->user->access_keys.end()) {
364 ldout(s->cct, 10) << "ERROR: access key not encoded in user info" << dendl;
365 return -EPERM;
366 }
367
368 RGWAccessKey& k = iter->second;
369
370 string secret_key = "AWS4" + k.key;
371
372 char secret_k[secret_key.size() * MAX_UTF8_SZ];
373
374 size_t n = 0;
375
376 for (size_t i = 0; i < secret_key.size(); i++) {
377 n += encode_utf8(secret_key[i], (unsigned char *) (secret_k + n));
378 }
379
380 string secret_key_utf8_k(secret_k, n);
381
382 /* date */
383
384 char date_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
385 calc_hmac_sha256(secret_key_utf8_k.c_str(), secret_key_utf8_k.size(),
386 date.c_str(), date.size(), date_k);
387
388 char aux[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE * 2 + 1];
389 buf_to_hex((unsigned char *) date_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux);
390
391 ldout(s->cct, 10) << "date_k = " << string(aux) << dendl;
392
393 /* region */
394
395 char region_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
396 calc_hmac_sha256(date_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, region.c_str(), region.size(), region_k);
397
398 buf_to_hex((unsigned char *) region_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux);
399
400 ldout(s->cct, 10) << "region_k = " << string(aux) << dendl;
401
402 /* service */
403
404 char service_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
405 calc_hmac_sha256(region_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, service.c_str(), service.size(), service_k);
406
407 buf_to_hex((unsigned char *) service_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux);
408
409 ldout(s->cct, 10) << "service_k = " << string(aux) << dendl;
410
411 /* aws4_request */
412
413 char *signing_k = s->aws4_auth->signing_k;
414
415 calc_hmac_sha256(service_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, "aws4_request", 12, signing_k);
416
417 buf_to_hex((unsigned char *) signing_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux);
418
419 ldout(s->cct, 10) << "signing_k = " << string(aux) << dendl;
420
421 s->aws4_auth->signing_key = aux;
422
423 /* new signature */
424
425 char signature_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
426 calc_hmac_sha256(signing_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, string_to_sign.c_str(), string_to_sign.size(), signature_k);
427
428 buf_to_hex((unsigned char *) signature_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux);
429
430 ldout(s->cct, 10) << "signature_k = " << string(aux) << dendl;
431
432 signature = string(aux);
433
434 ldout(s->cct, 10) << "new signature = " << signature << dendl;
435
436 return 0;
437 }