Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / rgw / rgw_rest_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 <errno.h>
5 #include <array>
6 #include <string.h>
7
8 #include "common/ceph_crypto.h"
9 #include "common/Formatter.h"
10 #include "common/utf8.h"
11 #include "common/ceph_json.h"
12 #include "common/safe_io.h"
13 #include "common/backport14.h"
14 #include <boost/algorithm/string.hpp>
15 #include <boost/algorithm/string/replace.hpp>
16 #include <boost/utility/string_view.hpp>
17
18 #include "rgw_rest.h"
19 #include "rgw_rest_s3.h"
20 #include "rgw_rest_s3website.h"
21 #include "rgw_auth_s3.h"
22 #include "rgw_acl.h"
23 #include "rgw_policy_s3.h"
24 #include "rgw_user.h"
25 #include "rgw_cors.h"
26 #include "rgw_cors_s3.h"
27 #include "rgw_tag_s3.h"
28
29 #include "rgw_client_io.h"
30
31 #include "rgw_keystone.h"
32 #include "rgw_auth_keystone.h"
33 #include "rgw_auth_registry.h"
34
35 #include "rgw_es_query.h"
36
37 #include <typeinfo> // for 'typeid'
38
39 #include "rgw_ldap.h"
40 #include "rgw_token.h"
41 #include "rgw_rest_role.h"
42 #include "rgw_crypt.h"
43 #include "rgw_crypt_sanitize.h"
44
45 #include "include/assert.h"
46
47 #define dout_context g_ceph_context
48 #define dout_subsys ceph_subsys_rgw
49
50 using namespace rgw;
51 using namespace ceph::crypto;
52
53 using std::get;
54
55 void list_all_buckets_start(struct req_state *s)
56 {
57   s->formatter->open_array_section_in_ns("ListAllMyBucketsResult", XMLNS_AWS_S3);
58 }
59
60 void list_all_buckets_end(struct req_state *s)
61 {
62   s->formatter->close_section();
63 }
64
65 void dump_bucket(struct req_state *s, RGWBucketEnt& obj)
66 {
67   s->formatter->open_object_section("Bucket");
68   s->formatter->dump_string("Name", obj.bucket.name);
69   dump_time(s, "CreationDate", &obj.creation_time);
70   s->formatter->close_section();
71 }
72
73 void rgw_get_errno_s3(rgw_http_error *e , int err_no)
74 {
75   rgw_http_errors::const_iterator r = rgw_http_s3_errors.find(err_no);
76
77   if (r != rgw_http_s3_errors.end()) {
78     e->http_ret = r->second.first;
79     e->s3_code = r->second.second;
80   } else {
81     e->http_ret = 500;
82     e->s3_code = "UnknownError";
83   }
84 }
85
86 struct response_attr_param {
87   const char *param;
88   const char *http_attr;
89 };
90
91 static struct response_attr_param resp_attr_params[] = {
92   {"response-content-type", "Content-Type"},
93   {"response-content-language", "Content-Language"},
94   {"response-expires", "Expires"},
95   {"response-cache-control", "Cache-Control"},
96   {"response-content-disposition", "Content-Disposition"},
97   {"response-content-encoding", "Content-Encoding"},
98   {NULL, NULL},
99 };
100
101 int RGWGetObj_ObjStore_S3Website::send_response_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) {
102   map<string, bufferlist>::iterator iter;
103   iter = attrs.find(RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION);
104   if (iter != attrs.end()) {
105     bufferlist &bl = iter->second;
106     s->redirect = string(bl.c_str(), bl.length());
107     s->err.http_ret = 301;
108     ldout(s->cct, 20) << __CEPH_ASSERT_FUNCTION << " redirecting per x-amz-website-redirect-location=" << s->redirect << dendl;
109     op_ret = -ERR_WEBSITE_REDIRECT;
110     set_req_state_err(s, op_ret);
111     dump_errno(s);
112     dump_content_length(s, 0);
113     dump_redirect(s, s->redirect);
114     end_header(s, this);
115     return op_ret;
116   } else {
117     return RGWGetObj_ObjStore_S3::send_response_data(bl, bl_ofs, bl_len);
118   }
119 }
120
121 int RGWGetObj_ObjStore_S3Website::send_response_data_error()
122 {
123   return RGWGetObj_ObjStore_S3::send_response_data_error();
124 }
125
126 int RGWGetObj_ObjStore_S3::get_params()
127 {
128   // for multisite sync requests, only read the slo manifest itself, rather than
129   // all of the data from its parts. the parts will sync as separate objects
130   skip_manifest = s->info.args.exists(RGW_SYS_PARAM_PREFIX "sync-manifest");
131
132   // multisite sync requests should fetch encrypted data, along with the
133   // attributes needed to support decryption on the other zone
134   if (s->system_request) {
135     skip_decrypt = s->info.args.exists(RGW_SYS_PARAM_PREFIX "skip-decrypt");
136   }
137
138   return RGWGetObj_ObjStore::get_params();
139 }
140
141 int RGWGetObj_ObjStore_S3::send_response_data_error()
142 {
143   bufferlist bl;
144   return send_response_data(bl, 0 , 0);
145 }
146
147 template <class T>
148 int decode_attr_bl_single_value(map<string, bufferlist>& attrs, const char *attr_name, T *result, T def_val)
149 {
150   map<string, bufferlist>::iterator iter = attrs.find(attr_name);
151   if (iter == attrs.end()) {
152     *result = def_val;
153     return 0;
154   }
155   bufferlist& bl = iter->second;
156   if (bl.length() == 0) {
157     *result = def_val;
158     return 0;
159   }
160   bufferlist::iterator bliter = bl.begin();
161   try {
162     ::decode(*result, bliter);
163   } catch (buffer::error& err) {
164     return -EIO;
165   }
166   return 0;
167 }
168
169 int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
170                                               off_t bl_len)
171 {
172   const char *content_type = NULL;
173   string content_type_str;
174   map<string, string> response_attrs;
175   map<string, string>::iterator riter;
176   bufferlist metadata_bl;
177
178   if (sent_header)
179     goto send_data;
180
181   if (custom_http_ret) {
182     set_req_state_err(s, 0);
183     dump_errno(s, custom_http_ret);
184   } else {
185     set_req_state_err(s, (partial_content && !op_ret) ? STATUS_PARTIAL_CONTENT
186                   : op_ret);
187     dump_errno(s);
188   }
189
190   if (op_ret)
191     goto done;
192
193   if (range_str)
194     dump_range(s, start, end, s->obj_size);
195
196   if (s->system_request &&
197       s->info.args.exists(RGW_SYS_PARAM_PREFIX "prepend-metadata")) {
198
199     dump_header(s, "Rgwx-Object-Size", (long long)total_len);
200
201     if (rgwx_stat) {
202       /*
203        * in this case, we're not returning the object's content, only the prepended
204        * extra metadata
205        */
206       total_len = 0;
207     }
208
209     /* JSON encode object metadata */
210     JSONFormatter jf;
211     jf.open_object_section("obj_metadata");
212     encode_json("attrs", attrs, &jf);
213     utime_t ut(lastmod);
214     encode_json("mtime", ut, &jf);
215     jf.close_section();
216     stringstream ss;
217     jf.flush(ss);
218     metadata_bl.append(ss.str());
219     dump_header(s, "Rgwx-Embedded-Metadata-Len", metadata_bl.length());
220     total_len += metadata_bl.length();
221   }
222
223   if (s->system_request && !real_clock::is_zero(lastmod)) {
224     /* we end up dumping mtime in two different methods, a bit redundant */
225     dump_epoch_header(s, "Rgwx-Mtime", lastmod);
226     uint64_t pg_ver = 0;
227     int r = decode_attr_bl_single_value(attrs, RGW_ATTR_PG_VER, &pg_ver, (uint64_t)0);
228     if (r < 0) {
229       ldout(s->cct, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
230     }
231     dump_header(s, "Rgwx-Obj-PG-Ver", pg_ver);
232
233     uint32_t source_zone_short_id = 0;
234     r = decode_attr_bl_single_value(attrs, RGW_ATTR_SOURCE_ZONE, &source_zone_short_id, (uint32_t)0);
235     if (r < 0) {
236       ldout(s->cct, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
237     }
238     if (source_zone_short_id != 0) {
239       dump_header(s, "Rgwx-Source-Zone-Short-Id", source_zone_short_id);
240     }
241   }
242
243   for (auto &it : crypt_http_responses)
244     dump_header(s, it.first, it.second);
245
246   dump_content_length(s, total_len);
247   dump_last_modified(s, lastmod);
248   if (!version_id.empty()) {
249     dump_header(s, "x-amz-version-id", version_id);
250   }
251   
252
253   if (! op_ret) {
254     if (! lo_etag.empty()) {
255       /* Handle etag of Swift API's large objects (DLO/SLO). It's entirerly
256        * legit to perform GET on them through S3 API. In such situation,
257        * a client should receive the composited content with corresponding
258        * etag value. */
259       dump_etag(s, lo_etag);
260     } else {
261       auto iter = attrs.find(RGW_ATTR_ETAG);
262       if (iter != attrs.end()) {
263         dump_etag(s, iter->second);
264       }
265     }
266
267     for (struct response_attr_param *p = resp_attr_params; p->param; p++) {
268       bool exists;
269       string val = s->info.args.get(p->param, &exists);
270       if (exists) {
271         if (strcmp(p->param, "response-content-type") != 0) {
272           response_attrs[p->http_attr] = val;
273         } else {
274           content_type_str = val;
275           content_type = content_type_str.c_str();
276         }
277       }
278     }
279
280     for (auto iter = attrs.begin(); iter != attrs.end(); ++iter) {
281       const char *name = iter->first.c_str();
282       map<string, string>::iterator aiter = rgw_to_http_attrs.find(name);
283       if (aiter != rgw_to_http_attrs.end()) {
284         if (response_attrs.count(aiter->second) == 0) {
285           /* Was not already overridden by a response param. */
286           response_attrs[aiter->second] = iter->second.c_str();
287         }
288       } else if (iter->first.compare(RGW_ATTR_CONTENT_TYPE) == 0) {
289         /* Special handling for content_type. */
290         if (!content_type) {
291           content_type = iter->second.c_str();
292         }
293       } else if (strcmp(name, RGW_ATTR_SLO_UINDICATOR) == 0) {
294         // this attr has an extra length prefix from ::encode() in prior versions
295         dump_header(s, "X-Object-Meta-Static-Large-Object", "True");
296       } else if (strncmp(name, RGW_ATTR_META_PREFIX,
297                          sizeof(RGW_ATTR_META_PREFIX)-1) == 0) {
298         /* User custom metadata. */
299         name += sizeof(RGW_ATTR_PREFIX) - 1;
300         dump_header(s, name, iter->second);
301       } else if (iter->first.compare(RGW_ATTR_TAGS) == 0) {
302         RGWObjTags obj_tags;
303         try{
304           bufferlist::iterator it = iter->second.begin();
305           obj_tags.decode(it);
306         } catch (buffer::error &err) {
307           ldout(s->cct,0) << "Error caught buffer::error couldn't decode TagSet " << dendl;
308         }
309         dump_header(s, RGW_AMZ_TAG_COUNT, obj_tags.count());
310       }
311     }
312   }
313
314 done:
315   for (riter = response_attrs.begin(); riter != response_attrs.end();
316        ++riter) {
317     dump_header(s, riter->first, riter->second);
318   }
319
320   if (op_ret == -ERR_NOT_MODIFIED) {
321       end_header(s, this);
322   } else {
323       if (!content_type)
324           content_type = "binary/octet-stream";
325
326       end_header(s, this, content_type);
327   }
328
329   if (metadata_bl.length()) {
330     dump_body(s, metadata_bl);
331   }
332   sent_header = true;
333
334 send_data:
335   if (get_data && !op_ret) {
336     int r = dump_body(s, bl.c_str() + bl_ofs, bl_len);
337     if (r < 0)
338       return r;
339   }
340
341   return 0;
342 }
343
344 int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr<RGWGetDataCB> *filter, RGWGetDataCB* cb, bufferlist* manifest_bl)
345 {
346   if (skip_decrypt) { // bypass decryption for multisite sync requests
347     return 0;
348   }
349
350   int res = 0;
351   std::unique_ptr<BlockCrypt> block_crypt;
352   res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses);
353   if (res == 0) {
354     if (block_crypt != nullptr) {
355       auto f = std::unique_ptr<RGWGetObj_BlockDecrypt>(new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt)));
356       //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
357       if (f != nullptr) {
358         if (manifest_bl != nullptr) {
359           res = f->read_manifest(*manifest_bl);
360           if (res == 0) {
361             *filter = std::move(f);
362           }
363         }
364       }
365     }
366   }
367   return res;
368 }
369
370 void RGWGetObjTags_ObjStore_S3::send_response_data(bufferlist& bl)
371 {
372   dump_errno(s);
373   end_header(s, this, "application/xml");
374   dump_start(s);
375
376   s->formatter->open_object_section_in_ns("Tagging", XMLNS_AWS_S3);
377   s->formatter->open_object_section("TagSet");
378   if (has_tags){
379     RGWObjTagSet_S3 tagset;
380     bufferlist::iterator iter = bl.begin();
381     try {
382       tagset.decode(iter);
383     } catch (buffer::error& err) {
384       ldout(s->cct,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl;
385       op_ret= -EIO;
386       return;
387     }
388     tagset.dump_xml(s->formatter);
389   }
390   s->formatter->close_section();
391   s->formatter->close_section();
392   rgw_flush_formatter_and_reset(s, s->formatter);
393 }
394
395
396 int RGWPutObjTags_ObjStore_S3::get_params()
397 {
398   RGWObjTagsXMLParser parser;
399
400   if (!parser.init()){
401     return -EINVAL;
402   }
403
404   char *data=nullptr;
405   int len=0;
406
407   const auto max_size = s->cct->_conf->rgw_max_put_param_size;
408   int r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
409
410   if (r < 0)
411     return r;
412
413   auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
414
415   if (!parser.parse(data, len, 1)) {
416     return -ERR_MALFORMED_XML;
417   }
418
419   RGWObjTagSet_S3 *obj_tags_s3;
420   RGWObjTagging_S3 *tagging;
421
422   tagging = static_cast<RGWObjTagging_S3 *>(parser.find_first("Tagging"));
423   obj_tags_s3 = static_cast<RGWObjTagSet_S3 *>(tagging->find_first("TagSet"));
424   if(!obj_tags_s3){
425     return -ERR_MALFORMED_XML;
426   }
427
428   RGWObjTags obj_tags;
429   r = obj_tags_s3->rebuild(obj_tags);
430   if (r < 0)
431     return r;
432
433   obj_tags.encode(tags_bl);
434   ldout(s->cct, 20) << "Read " << obj_tags.count() << "tags" << dendl;
435
436   return 0;
437 }
438
439 void RGWPutObjTags_ObjStore_S3::send_response()
440 {
441   if (op_ret)
442     set_req_state_err(s, op_ret);
443   dump_errno(s);
444   end_header(s, this, "application/xml");
445   dump_start(s);
446
447 }
448
449 void RGWDeleteObjTags_ObjStore_S3::send_response()
450 {
451   int r = op_ret;
452   if (r == -ENOENT)
453     r = 0;
454   if (!r)
455     r = STATUS_NO_CONTENT;
456
457   set_req_state_err(s, r);
458   dump_errno(s);
459   end_header(s, this);
460 }
461
462 void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets)
463 {
464   if (op_ret)
465     set_req_state_err(s, op_ret);
466   dump_errno(s);
467   dump_start(s);
468   end_header(s, NULL, "application/xml");
469
470   if (! op_ret) {
471     list_all_buckets_start(s);
472     dump_owner(s, s->user->user_id, s->user->display_name);
473     s->formatter->open_array_section("Buckets");
474     sent_data = true;
475   }
476 }
477
478 void RGWListBuckets_ObjStore_S3::send_response_data(RGWUserBuckets& buckets)
479 {
480   if (!sent_data)
481     return;
482
483   map<string, RGWBucketEnt>& m = buckets.get_buckets();
484   map<string, RGWBucketEnt>::iterator iter;
485
486   for (iter = m.begin(); iter != m.end(); ++iter) {
487     RGWBucketEnt obj = iter->second;
488     dump_bucket(s, obj);
489   }
490   rgw_flush_formatter(s, s->formatter);
491 }
492
493 void RGWListBuckets_ObjStore_S3::send_response_end()
494 {
495   if (sent_data) {
496     s->formatter->close_section();
497     list_all_buckets_end(s);
498     rgw_flush_formatter_and_reset(s, s->formatter);
499   }
500 }
501
502 int RGWGetUsage_ObjStore_S3::get_params()
503 {
504   start_date = s->info.args.get("start-date");
505   end_date = s->info.args.get("end-date"); 
506   return 0;
507 }
508
509 static void dump_usage_categories_info(Formatter *formatter, const rgw_usage_log_entry& entry, map<string, bool> *categories)
510 {
511   formatter->open_array_section("categories");
512   map<string, rgw_usage_data>::const_iterator uiter;
513   for (uiter = entry.usage_map.begin(); uiter != entry.usage_map.end(); ++uiter) {
514     if (categories && !categories->empty() && !categories->count(uiter->first))
515       continue;
516     const rgw_usage_data& usage = uiter->second;
517     formatter->open_object_section("Entry");
518     formatter->dump_string("Category", uiter->first);
519     formatter->dump_int("BytesSent", usage.bytes_sent);
520     formatter->dump_int("BytesReceived", usage.bytes_received);
521     formatter->dump_int("Ops", usage.ops);
522     formatter->dump_int("SuccessfulOps", usage.successful_ops);
523     formatter->close_section(); // Entry
524   }
525   formatter->close_section(); // Category
526 }
527
528 static void dump_usage_bucket_info(Formatter *formatter, const std::string& name, const cls_user_bucket_entry& entry)
529 {
530   formatter->open_object_section("Entry");
531   formatter->dump_string("Bucket", name);
532   formatter->dump_int("Bytes", entry.size);
533   formatter->dump_int("Bytes_Rounded", entry.size_rounded);
534   formatter->close_section(); // entry
535 }
536
537 void RGWGetUsage_ObjStore_S3::send_response()
538 {
539   if (op_ret < 0)
540     set_req_state_err(s, op_ret);
541   dump_errno(s);
542
543   end_header(s, this, "application/xml");
544   dump_start(s);
545   if (op_ret < 0)
546     return;
547
548   Formatter *formatter = s->formatter;
549   string last_owner;
550   bool user_section_open = false;
551   
552   formatter->open_object_section("Usage");
553   if (show_log_entries) {
554     formatter->open_array_section("Entries");
555   }
556   map<rgw_user_bucket, rgw_usage_log_entry>::iterator iter;
557   for (iter = usage.begin(); iter != usage.end(); ++iter) {
558     const rgw_user_bucket& ub = iter->first;
559     const rgw_usage_log_entry& entry = iter->second;
560
561     if (show_log_entries) {
562       if (ub.user.compare(last_owner) != 0) {
563         if (user_section_open) {
564           formatter->close_section();
565           formatter->close_section();
566         }
567         formatter->open_object_section("User");
568         formatter->dump_string("Owner", ub.user);
569         formatter->open_array_section("Buckets");
570         user_section_open = true;
571         last_owner = ub.user;
572       }
573       formatter->open_object_section("Bucket");
574       formatter->dump_string("Bucket", ub.bucket);
575       utime_t ut(entry.epoch, 0);
576       ut.gmtime(formatter->dump_stream("Time"));
577       formatter->dump_int("Epoch", entry.epoch);
578       dump_usage_categories_info(formatter, entry, &categories);
579       formatter->close_section(); // bucket
580     }
581
582     summary_map[ub.user].aggregate(entry, &categories);
583   }
584
585   if (show_log_entries) {
586      if (user_section_open) {
587        formatter->close_section(); // buckets
588        formatter->close_section(); //user
589      }
590      formatter->close_section(); // entries
591    }
592
593    if (show_log_sum) {
594      formatter->open_array_section("Summary");
595      map<string, rgw_usage_log_entry>::iterator siter;
596      for (siter = summary_map.begin(); siter != summary_map.end(); ++siter) {
597        const rgw_usage_log_entry& entry = siter->second;
598        formatter->open_object_section("User");
599        formatter->dump_string("User", siter->first);
600        dump_usage_categories_info(formatter, entry, &categories);
601        rgw_usage_data total_usage;
602        entry.sum(total_usage, categories);
603        formatter->open_object_section("Total");
604        formatter->dump_int("BytesSent", total_usage.bytes_sent);
605        formatter->dump_int("BytesReceived", total_usage.bytes_received);
606        formatter->dump_int("Ops", total_usage.ops);
607        formatter->dump_int("SuccessfulOps", total_usage.successful_ops);
608        formatter->close_section(); // total
609        formatter->close_section(); // user
610      }
611
612      if (s->cct->_conf->rgw_rest_getusage_op_compat) {
613        formatter->open_object_section("Stats");
614      }
615
616      formatter->dump_int("TotalBytes", header.stats.total_bytes);
617      formatter->dump_int("TotalBytesRounded", header.stats.total_bytes_rounded);
618      formatter->dump_int("TotalEntries", header.stats.total_entries);
619
620      if (s->cct->_conf->rgw_rest_getusage_op_compat) {
621        formatter->close_section(); //Stats
622      }
623
624      formatter->close_section(); // summary
625    }
626
627   formatter->open_array_section("CapacityUsed");
628   formatter->open_object_section("User");
629   formatter->open_array_section("Buckets");
630   for (const auto& biter : buckets_usage) {
631     const cls_user_bucket_entry& entry = biter.second;
632     dump_usage_bucket_info(formatter, biter.first, entry);
633   }
634   formatter->close_section(); // Buckets
635   formatter->close_section(); // User
636   formatter->close_section(); // CapacityUsed
637
638   formatter->close_section(); // usage
639   rgw_flush_formatter_and_reset(s, s->formatter);
640 }
641
642 int RGWListBucket_ObjStore_S3::get_params()
643 {
644   list_versions = s->info.args.exists("versions");
645   prefix = s->info.args.get("prefix");
646   if (!list_versions) {
647     marker = s->info.args.get("marker");
648   } else {
649     marker.name = s->info.args.get("key-marker");
650     marker.instance = s->info.args.get("version-id-marker");
651   }
652   max_keys = s->info.args.get("max-keys");
653   op_ret = parse_max_keys();
654   if (op_ret < 0) {
655     return op_ret;
656   }
657   delimiter = s->info.args.get("delimiter");
658   encoding_type = s->info.args.get("encoding-type");
659   if (s->system_request) {
660     s->info.args.get_bool("objs-container", &objs_container, false);
661     const char *shard_id_str = s->info.env->get("HTTP_RGWX_SHARD_ID");
662     if (shard_id_str) {
663       string err;
664       shard_id = strict_strtol(shard_id_str, 10, &err);
665       if (!err.empty()) {
666         ldout(s->cct, 5) << "bad shard id specified: " << shard_id_str << dendl;
667         return -EINVAL;
668       }
669     } else {
670       shard_id = s->bucket_instance_shard_id;
671     }
672   }
673   return 0;
674 }
675
676 void RGWListBucket_ObjStore_S3::send_versioned_response()
677 {
678   s->formatter->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3);
679   if (!s->bucket_tenant.empty())
680     s->formatter->dump_string("Tenant", s->bucket_tenant);
681   s->formatter->dump_string("Name", s->bucket_name);
682   s->formatter->dump_string("Prefix", prefix);
683   s->formatter->dump_string("KeyMarker", marker.name);
684   s->formatter->dump_string("VersionIdMarker", marker.instance);
685   if (is_truncated && !next_marker.empty()) {
686     s->formatter->dump_string("NextKeyMarker", next_marker.name);
687     s->formatter->dump_string("NextVersionIdMarker", next_marker.instance);
688   }
689   s->formatter->dump_int("MaxKeys", max);
690   if (!delimiter.empty())
691     s->formatter->dump_string("Delimiter", delimiter);
692
693   s->formatter->dump_string("IsTruncated", (max && is_truncated ? "true"
694                                             : "false"));
695
696   bool encode_key = false;
697   if (strcasecmp(encoding_type.c_str(), "url") == 0) {
698     s->formatter->dump_string("EncodingType", "url");
699     encode_key = true;
700   }
701
702   if (op_ret >= 0) {
703     if (objs_container) {
704       s->formatter->open_array_section("Entries");
705     }
706
707     vector<rgw_bucket_dir_entry>::iterator iter;
708     for (iter = objs.begin(); iter != objs.end(); ++iter) {
709       const char *section_name = (iter->is_delete_marker() ? "DeleteMarker"
710                                   : "Version");
711       s->formatter->open_object_section(section_name);
712       if (objs_container) {
713         s->formatter->dump_bool("IsDeleteMarker", iter->is_delete_marker());
714       }
715       rgw_obj_key key(iter->key);
716       if (encode_key) {
717         string key_name;
718         url_encode(key.name, key_name);
719         s->formatter->dump_string("Key", key_name);
720       } else {
721         s->formatter->dump_string("Key", key.name);
722       }
723       string version_id = key.instance;
724       if (version_id.empty()) {
725         version_id = "null";
726       }
727       if (s->system_request) {
728         if (iter->versioned_epoch > 0) {
729           s->formatter->dump_int("VersionedEpoch", iter->versioned_epoch);
730         }
731         s->formatter->dump_string("RgwxTag", iter->tag);
732         utime_t ut(iter->meta.mtime);
733         ut.gmtime_nsec(s->formatter->dump_stream("RgwxMtime"));
734       }
735       s->formatter->dump_string("VersionId", version_id);
736       s->formatter->dump_bool("IsLatest", iter->is_current());
737       dump_time(s, "LastModified", &iter->meta.mtime);
738       if (!iter->is_delete_marker()) {
739         s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
740         s->formatter->dump_int("Size", iter->meta.accounted_size);
741         s->formatter->dump_string("StorageClass", "STANDARD");
742       }
743       dump_owner(s, iter->meta.owner, iter->meta.owner_display_name);
744       s->formatter->close_section();
745     }
746     if (objs_container) {
747       s->formatter->close_section();
748     }
749
750     if (!common_prefixes.empty()) {
751       map<string, bool>::iterator pref_iter;
752       for (pref_iter = common_prefixes.begin();
753            pref_iter != common_prefixes.end(); ++pref_iter) {
754         s->formatter->open_array_section("CommonPrefixes");
755         s->formatter->dump_string("Prefix", pref_iter->first);
756         s->formatter->close_section();
757       }
758     }
759   }
760   s->formatter->close_section();
761   rgw_flush_formatter_and_reset(s, s->formatter);
762 }
763
764 void RGWListBucket_ObjStore_S3::send_response()
765 {
766   if (op_ret < 0)
767     set_req_state_err(s, op_ret);
768   dump_errno(s);
769
770   end_header(s, this, "application/xml");
771   dump_start(s);
772   if (op_ret < 0)
773     return;
774
775   if (list_versions) {
776     send_versioned_response();
777     return;
778   }
779
780   s->formatter->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3);
781   if (!s->bucket_tenant.empty())
782     s->formatter->dump_string("Tenant", s->bucket_tenant);
783   s->formatter->dump_string("Name", s->bucket_name);
784   s->formatter->dump_string("Prefix", prefix);
785   s->formatter->dump_string("Marker", marker.name);
786   if (is_truncated && !next_marker.empty())
787     s->formatter->dump_string("NextMarker", next_marker.name);
788   s->formatter->dump_int("MaxKeys", max);
789   if (!delimiter.empty())
790     s->formatter->dump_string("Delimiter", delimiter);
791
792   s->formatter->dump_string("IsTruncated", (max && is_truncated ? "true"
793                                             : "false"));
794
795   bool encode_key = false;
796   if (strcasecmp(encoding_type.c_str(), "url") == 0) {
797     s->formatter->dump_string("EncodingType", "url");
798     encode_key = true;
799   }
800
801   if (op_ret >= 0) {
802     vector<rgw_bucket_dir_entry>::iterator iter;
803     for (iter = objs.begin(); iter != objs.end(); ++iter) {
804       rgw_obj_key key(iter->key);
805       s->formatter->open_array_section("Contents");
806       if (encode_key) {
807         string key_name;
808         url_encode(key.name, key_name);
809         s->formatter->dump_string("Key", key_name);
810       } else {
811         s->formatter->dump_string("Key", key.name);
812       }
813       dump_time(s, "LastModified", &iter->meta.mtime);
814       s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
815       s->formatter->dump_int("Size", iter->meta.accounted_size);
816       s->formatter->dump_string("StorageClass", "STANDARD");
817       dump_owner(s, iter->meta.owner, iter->meta.owner_display_name);
818       if (s->system_request) {
819         s->formatter->dump_string("RgwxTag", iter->tag);
820       }
821       s->formatter->close_section();
822     }
823     if (!common_prefixes.empty()) {
824       map<string, bool>::iterator pref_iter;
825       for (pref_iter = common_prefixes.begin();
826            pref_iter != common_prefixes.end(); ++pref_iter) {
827         s->formatter->open_array_section("CommonPrefixes");
828         s->formatter->dump_string("Prefix", pref_iter->first);
829         s->formatter->close_section();
830       }
831     }
832   }
833   s->formatter->close_section();
834   rgw_flush_formatter_and_reset(s, s->formatter);
835 }
836
837 void RGWGetBucketLogging_ObjStore_S3::send_response()
838 {
839   dump_errno(s);
840   end_header(s, this, "application/xml");
841   dump_start(s);
842
843   s->formatter->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3);
844   s->formatter->close_section();
845   rgw_flush_formatter_and_reset(s, s->formatter);
846 }
847
848 void RGWGetBucketLocation_ObjStore_S3::send_response()
849 {
850   dump_errno(s);
851   end_header(s, this);
852   dump_start(s);
853
854   RGWZoneGroup zonegroup;
855   string api_name;
856
857   int ret = store->get_zonegroup(s->bucket_info.zonegroup, zonegroup);
858   if (ret >= 0) {
859     api_name = zonegroup.api_name;
860   } else  {
861     if (s->bucket_info.zonegroup != "default") {
862       api_name = s->bucket_info.zonegroup;
863     }
864   }
865
866   s->formatter->dump_format_ns("LocationConstraint", XMLNS_AWS_S3,
867                                "%s", api_name.c_str());
868   rgw_flush_formatter_and_reset(s, s->formatter);
869 }
870
871 void RGWGetBucketVersioning_ObjStore_S3::send_response()
872 {
873   dump_errno(s);
874   end_header(s, this, "application/xml");
875   dump_start(s);
876
877   s->formatter->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3);
878   if (versioned) {
879     const char *status = (versioning_enabled ? "Enabled" : "Suspended");
880     s->formatter->dump_string("Status", status);
881   }
882   s->formatter->close_section();
883   rgw_flush_formatter_and_reset(s, s->formatter);
884 }
885
886 class RGWSetBucketVersioningParser : public RGWXMLParser
887 {
888   XMLObj *alloc_obj(const char *el) override {
889     return new XMLObj;
890   }
891
892 public:
893   RGWSetBucketVersioningParser() {}
894   ~RGWSetBucketVersioningParser() override {}
895
896   int get_versioning_status(bool *status) {
897     XMLObj *config = find_first("VersioningConfiguration");
898     if (!config)
899       return -EINVAL;
900
901     *status = false;
902
903     XMLObj *field = config->find_first("Status");
904     if (!field)
905       return 0;
906
907     string& s = field->get_data();
908
909     if (stringcasecmp(s, "Enabled") == 0) {
910       *status = true;
911     } else if (stringcasecmp(s, "Suspended") != 0) {
912       return -EINVAL;
913     }
914
915     return 0;
916   }
917 };
918
919 int RGWSetBucketVersioning_ObjStore_S3::get_params()
920 {
921   char *data = nullptr;
922   int len = 0;
923   int r =
924     rgw_rest_read_all_input(s, &data, &len, s->cct->_conf->rgw_max_put_param_size, false);
925   if (r < 0) {
926     return r;
927   }
928   
929   auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
930
931   r = do_aws4_auth_completion();
932   if (r < 0) {
933     return r;
934   }
935
936   RGWSetBucketVersioningParser parser;
937
938   if (!parser.init()) {
939     ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
940     r = -EIO;
941     return r;
942   }
943
944   if (!parser.parse(data, len, 1)) {
945     ldout(s->cct, 10) << "failed to parse data: " << data << dendl;
946     r = -EINVAL;
947     return r;
948   }
949
950   if (!store->is_meta_master()) {
951     /* only need to keep this data around if we're not meta master */
952     in_data.append(data, len);
953   }
954
955   r = parser.get_versioning_status(&enable_versioning);
956   
957   return r;
958 }
959
960 void RGWSetBucketVersioning_ObjStore_S3::send_response()
961 {
962   if (op_ret)
963     set_req_state_err(s, op_ret);
964   dump_errno(s);
965   end_header(s);
966 }
967
968 int RGWSetBucketWebsite_ObjStore_S3::get_params()
969 {
970   char *data = nullptr;
971   int len = 0;
972   const auto max_size = s->cct->_conf->rgw_max_put_param_size;
973   int r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
974
975   if (r < 0) {
976     return r;
977   }
978
979   auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
980
981   r = do_aws4_auth_completion();
982   if (r < 0) {
983     return r;
984   }
985
986   bufferptr in_ptr(data, len);
987   in_data.append(in_ptr);
988
989   RGWXMLDecoder::XMLParser parser;
990   if (!parser.init()) {
991     ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
992     return -EIO;
993   }
994
995   if (!parser.parse(data, len, 1)) {
996     string str(data, len);
997     ldout(s->cct, 5) << "failed to parse xml: " << str << dendl;
998     return -EINVAL;
999   }
1000
1001   try {
1002     RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf, &parser, true);
1003   } catch (RGWXMLDecoder::err& err) {
1004     string str(data, len);
1005     ldout(s->cct, 5) << "unexpected xml: " << str << dendl;
1006     return -EINVAL;
1007   }
1008
1009   return 0;
1010 }
1011
1012 void RGWSetBucketWebsite_ObjStore_S3::send_response()
1013 {
1014   if (op_ret < 0)
1015     set_req_state_err(s, op_ret);
1016   dump_errno(s);
1017   end_header(s);
1018 }
1019
1020 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
1021 {
1022   if (op_ret == 0) {
1023     op_ret = STATUS_NO_CONTENT;
1024   }
1025   set_req_state_err(s, op_ret);
1026   dump_errno(s);
1027   end_header(s);
1028 }
1029
1030 void RGWGetBucketWebsite_ObjStore_S3::send_response()
1031 {
1032   if (op_ret)
1033     set_req_state_err(s, op_ret);
1034   dump_errno(s);
1035   end_header(s, this, "application/xml");
1036   dump_start(s);
1037
1038   if (op_ret < 0) {
1039     return;
1040   }
1041
1042   RGWBucketWebsiteConf& conf = s->bucket_info.website_conf;
1043
1044   s->formatter->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3);
1045   conf.dump_xml(s->formatter);
1046   s->formatter->close_section(); // WebsiteConfiguration
1047   rgw_flush_formatter_and_reset(s, s->formatter);
1048 }
1049
1050 static void dump_bucket_metadata(struct req_state *s, RGWBucketEnt& bucket)
1051 {
1052   dump_header(s, "X-RGW-Object-Count", static_cast<long long>(bucket.count));
1053   dump_header(s, "X-RGW-Bytes-Used", static_cast<long long>(bucket.size));
1054 }
1055
1056 void RGWStatBucket_ObjStore_S3::send_response()
1057 {
1058   if (op_ret >= 0) {
1059     dump_bucket_metadata(s, bucket);
1060   }
1061
1062   set_req_state_err(s, op_ret);
1063   dump_errno(s);
1064
1065   end_header(s, this);
1066   dump_start(s);
1067 }
1068
1069 static int create_s3_policy(struct req_state *s, RGWRados *store,
1070                             RGWAccessControlPolicy_S3& s3policy,
1071                             ACLOwner& owner)
1072 {
1073   if (s->has_acl_header) {
1074     if (!s->canned_acl.empty())
1075       return -ERR_INVALID_REQUEST;
1076
1077     return s3policy.create_from_headers(store, s->info.env, owner);
1078   }
1079
1080   return s3policy.create_canned(owner, s->bucket_owner, s->canned_acl);
1081 }
1082
1083 class RGWLocationConstraint : public XMLObj
1084 {
1085 public:
1086   RGWLocationConstraint() {}
1087   ~RGWLocationConstraint() override {}
1088   bool xml_end(const char *el) override {
1089     if (!el)
1090       return false;
1091
1092     location_constraint = get_data();
1093
1094     return true;
1095   }
1096
1097   string location_constraint;
1098 };
1099
1100 class RGWCreateBucketConfig : public XMLObj
1101 {
1102 public:
1103   RGWCreateBucketConfig() {}
1104   ~RGWCreateBucketConfig() override {}
1105 };
1106
1107 class RGWCreateBucketParser : public RGWXMLParser
1108 {
1109   XMLObj *alloc_obj(const char *el) override {
1110     return new XMLObj;
1111   }
1112
1113 public:
1114   RGWCreateBucketParser() {}
1115   ~RGWCreateBucketParser() override {}
1116
1117   bool get_location_constraint(string& zone_group) {
1118     XMLObj *config = find_first("CreateBucketConfiguration");
1119     if (!config)
1120       return false;
1121
1122     XMLObj *constraint = config->find_first("LocationConstraint");
1123     if (!constraint)
1124       return false;
1125
1126     zone_group = constraint->get_data();
1127
1128     return true;
1129   }
1130 };
1131
1132 int RGWCreateBucket_ObjStore_S3::get_params()
1133 {
1134   RGWAccessControlPolicy_S3 s3policy(s->cct);
1135
1136   int r = create_s3_policy(s, store, s3policy, s->owner);
1137   if (r < 0)
1138     return r;
1139
1140   policy = s3policy;
1141
1142   int len = 0;
1143   char *data = nullptr;
1144
1145   const auto max_size = s->cct->_conf->rgw_max_put_param_size;
1146   op_ret = rgw_rest_read_all_input(s, &data, &len, max_size, false);
1147
1148   if ((op_ret < 0) && (op_ret != -ERR_LENGTH_REQUIRED))
1149     return op_ret;
1150
1151   auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
1152
1153   const int auth_ret = do_aws4_auth_completion();
1154   if (auth_ret < 0) {
1155     return auth_ret;
1156   }
1157   
1158   bufferptr in_ptr(data, len);
1159   in_data.append(in_ptr);
1160
1161   if (len) {
1162     RGWCreateBucketParser parser;
1163
1164     if (!parser.init()) {
1165       ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
1166       return -EIO;
1167     }
1168
1169     bool success = parser.parse(data, len, 1);
1170     ldout(s->cct, 20) << "create bucket input data=" << data << dendl;
1171
1172     if (!success) {
1173       ldout(s->cct, 0) << "failed to parse input: " << data << dendl;
1174       return -EINVAL;
1175     }
1176
1177     if (!parser.get_location_constraint(location_constraint)) {
1178       ldout(s->cct, 0) << "provided input did not specify location constraint correctly" << dendl;
1179       return -EINVAL;
1180     }
1181
1182     ldout(s->cct, 10) << "create bucket location constraint: "
1183                       << location_constraint << dendl;
1184   }
1185
1186   size_t pos = location_constraint.find(':');
1187   if (pos != string::npos) {
1188     placement_rule = location_constraint.substr(pos + 1);
1189     location_constraint = location_constraint.substr(0, pos);
1190   }
1191
1192   return 0;
1193 }
1194
1195 void RGWCreateBucket_ObjStore_S3::send_response()
1196 {
1197   if (op_ret == -ERR_BUCKET_EXISTS)
1198     op_ret = 0;
1199   if (op_ret)
1200     set_req_state_err(s, op_ret);
1201   dump_errno(s);
1202   end_header(s);
1203
1204   if (op_ret < 0)
1205     return;
1206
1207   if (s->system_request) {
1208     JSONFormatter f; /* use json formatter for system requests output */
1209
1210     f.open_object_section("info");
1211     encode_json("entry_point_object_ver", ep_objv, &f);
1212     encode_json("object_ver", info.objv_tracker.read_version, &f);
1213     encode_json("bucket_info", info, &f);
1214     f.close_section();
1215     rgw_flush_formatter_and_reset(s, &f);
1216   }
1217 }
1218
1219 void RGWDeleteBucket_ObjStore_S3::send_response()
1220 {
1221   int r = op_ret;
1222   if (!r)
1223     r = STATUS_NO_CONTENT;
1224
1225   set_req_state_err(s, r);
1226   dump_errno(s);
1227   end_header(s, this);
1228
1229   if (s->system_request) {
1230     JSONFormatter f; /* use json formatter for system requests output */
1231
1232     f.open_object_section("info");
1233     encode_json("object_ver", objv_tracker.read_version, &f);
1234     f.close_section();
1235     rgw_flush_formatter_and_reset(s, &f);
1236   }
1237 }
1238
1239 int RGWPutObj_ObjStore_S3::get_params()
1240 {
1241   if (!s->length)
1242     return -ERR_LENGTH_REQUIRED;
1243
1244   RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
1245   map<string, bufferlist> src_attrs;
1246   size_t pos;
1247   int ret;
1248
1249   RGWAccessControlPolicy_S3 s3policy(s->cct);
1250   ret = create_s3_policy(s, store, s3policy, s->owner);
1251   if (ret < 0)
1252     return ret;
1253
1254   policy = s3policy;
1255
1256   if_match = s->info.env->get("HTTP_IF_MATCH");
1257   if_nomatch = s->info.env->get("HTTP_IF_NONE_MATCH");
1258   copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
1259   copy_source_range = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
1260
1261   /* handle x-amz-copy-source */
1262
1263   if (copy_source) {
1264     if (*copy_source == '/') ++copy_source;
1265     copy_source_bucket_name = copy_source;
1266     pos = copy_source_bucket_name.find("/");
1267     if (pos == std::string::npos) {
1268       ret = -EINVAL;
1269       ldout(s->cct, 5) << "x-amz-copy-source bad format" << dendl;
1270       return ret;
1271     }
1272     copy_source_object_name = copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
1273     copy_source_bucket_name = copy_source_bucket_name.substr(0, pos);
1274 #define VERSION_ID_STR "?versionId="
1275     pos = copy_source_object_name.find(VERSION_ID_STR);
1276     if (pos == std::string::npos) {
1277       copy_source_object_name = url_decode(copy_source_object_name);
1278     } else {
1279       copy_source_version_id = copy_source_object_name.substr(pos + sizeof(VERSION_ID_STR) - 1);
1280       copy_source_object_name = url_decode(copy_source_object_name.substr(0, pos));
1281     }
1282     pos = copy_source_bucket_name.find(":");
1283     if (pos == std::string::npos) {
1284        copy_source_tenant_name = s->src_tenant_name;
1285     } else {
1286        copy_source_tenant_name = copy_source_bucket_name.substr(0, pos);
1287        copy_source_bucket_name = copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
1288        if (copy_source_bucket_name.empty()) {
1289          ret = -EINVAL;
1290          ldout(s->cct, 5) << "source bucket name is empty" << dendl;
1291          return ret;
1292        }
1293     }
1294     ret = store->get_bucket_info(obj_ctx,
1295                                  copy_source_tenant_name,
1296                                  copy_source_bucket_name,
1297                                  copy_source_bucket_info,
1298                                  NULL, &src_attrs);
1299     if (ret < 0) {
1300       ldout(s->cct, 5) << __func__ << "(): get_bucket_info() returned ret=" << ret << dendl;
1301       return ret;
1302     }
1303
1304     /* handle x-amz-copy-source-range */
1305
1306     if (copy_source_range) {
1307       string range = copy_source_range;
1308       pos = range.find("=");
1309       if (pos == std::string::npos) {
1310         ret = -EINVAL;
1311         ldout(s->cct, 5) << "x-amz-copy-source-range bad format" << dendl;
1312         return ret;
1313       }
1314       range = range.substr(pos + 1);
1315       pos = range.find("-");
1316       if (pos == std::string::npos) {
1317         ret = -EINVAL;
1318         ldout(s->cct, 5) << "x-amz-copy-source-range bad format" << dendl;
1319         return ret;
1320       }
1321       string first = range.substr(0, pos);
1322       string last = range.substr(pos + 1);
1323       copy_source_range_fst = strtoull(first.c_str(), NULL, 10);
1324       copy_source_range_lst = strtoull(last.c_str(), NULL, 10);
1325     }
1326
1327   } /* copy_source */
1328
1329   /* handle object tagging */
1330   auto tag_str = s->info.env->get("HTTP_X_AMZ_TAGGING");
1331   if (tag_str){
1332     obj_tags = ceph::make_unique<RGWObjTags>();
1333     ret = obj_tags->set_from_string(tag_str);
1334     if (ret < 0){
1335       ldout(s->cct,0) << "setting obj tags failed with " << ret << dendl;
1336       if (ret == -ERR_INVALID_TAG){
1337         ret = -EINVAL; //s3 returns only -EINVAL for PUT requests
1338       }
1339
1340       return ret;
1341     }
1342   }
1343
1344   return RGWPutObj_ObjStore::get_params();
1345 }
1346
1347 int RGWPutObj_ObjStore_S3::get_data(bufferlist& bl)
1348 {
1349   const int ret = RGWPutObj_ObjStore::get_data(bl);
1350   if (ret == 0) {
1351     const int ret_auth = do_aws4_auth_completion();
1352     if (ret_auth < 0) {
1353       return ret_auth;
1354     }
1355   }
1356
1357   return ret;
1358 }
1359
1360 static int get_success_retcode(int code)
1361 {
1362   switch (code) {
1363     case 201:
1364       return STATUS_CREATED;
1365     case 204:
1366       return STATUS_NO_CONTENT;
1367   }
1368   return 0;
1369 }
1370
1371 void RGWPutObj_ObjStore_S3::send_response()
1372 {
1373   if (op_ret) {
1374     set_req_state_err(s, op_ret);
1375     dump_errno(s);
1376   } else {
1377     if (s->cct->_conf->rgw_s3_success_create_obj_status) {
1378       op_ret = get_success_retcode(
1379         s->cct->_conf->rgw_s3_success_create_obj_status);
1380       set_req_state_err(s, op_ret);
1381     }
1382     if (!copy_source) {
1383       dump_errno(s);
1384       dump_etag(s, etag);
1385       dump_content_length(s, 0);
1386       for (auto &it : crypt_http_responses)
1387         dump_header(s, it.first, it.second);
1388     } else {
1389       dump_errno(s);
1390       end_header(s, this, "application/xml");
1391       dump_start(s);
1392       struct tm tmp;
1393       utime_t ut(mtime);
1394       time_t secs = (time_t)ut.sec();
1395       gmtime_r(&secs, &tmp);
1396       char buf[TIME_BUF_SIZE];
1397       s->formatter->open_object_section_in_ns("CopyPartResult",
1398           "http://s3.amazonaws.com/doc/2006-03-01/");
1399       if (strftime(buf, sizeof(buf), "%Y-%m-%dT%T.000Z", &tmp) > 0) {
1400         s->formatter->dump_string("LastModified", buf);
1401       }
1402       s->formatter->dump_string("ETag", etag);
1403       s->formatter->close_section();
1404       rgw_flush_formatter_and_reset(s, s->formatter);
1405       return;
1406     }
1407   }
1408   if (s->system_request && !real_clock::is_zero(mtime)) {
1409     dump_epoch_header(s, "Rgwx-Mtime", mtime);
1410   }
1411   end_header(s, this);
1412 }
1413
1414 static inline int get_obj_attrs(RGWRados *store, struct req_state *s, rgw_obj& obj, map<string, bufferlist>& attrs)
1415 {
1416   RGWRados::Object op_target(store, s->bucket_info, *static_cast<RGWObjectCtx *>(s->obj_ctx), obj);
1417   RGWRados::Object::Read read_op(&op_target);
1418
1419   read_op.params.attrs = &attrs;
1420
1421   return read_op.prepare();
1422 }
1423
1424 static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const std::string& value)
1425 {
1426   bufferlist bl;
1427   ::encode(value,bl);
1428   attrs.emplace(key, std::move(bl));
1429 }
1430
1431 static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const char* value)
1432 {
1433   bufferlist bl;
1434   ::encode(value,bl);
1435   attrs.emplace(key, std::move(bl));
1436 }
1437
1438 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
1439     std::unique_ptr<RGWGetDataCB>* filter,
1440     RGWGetDataCB* cb,
1441     map<string, bufferlist>& attrs,
1442     bufferlist* manifest_bl)
1443 {
1444   std::map<std::string, std::string> crypt_http_responses_unused;
1445
1446   int res = 0;
1447   std::unique_ptr<BlockCrypt> block_crypt;
1448   res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses_unused);
1449   if (res == 0) {
1450     if (block_crypt != nullptr) {
1451       auto f = std::unique_ptr<RGWGetObj_BlockDecrypt>(new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt)));
1452       //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
1453       if (f != nullptr) {
1454         if (manifest_bl != nullptr) {
1455           res = f->read_manifest(*manifest_bl);
1456           if (res == 0) {
1457             *filter = std::move(f);
1458           }
1459         }
1460       }
1461     }
1462   }
1463   return res;
1464 }
1465
1466 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
1467     std::unique_ptr<RGWPutObjDataProcessor>* filter,
1468     RGWPutObjDataProcessor* cb)
1469 {
1470   int res = 0;
1471   RGWPutObjProcessor_Multipart* multi_processor=dynamic_cast<RGWPutObjProcessor_Multipart*>(cb);
1472   if (multi_processor != nullptr) {
1473     RGWMPObj* mp = nullptr;
1474     multi_processor->get_mp(&mp);
1475     if (mp != nullptr) {
1476       map<string, bufferlist> xattrs;
1477       string meta_oid;
1478       meta_oid = mp->get_meta();
1479
1480       rgw_obj obj;
1481       obj.init_ns(s->bucket, meta_oid, RGW_OBJ_NS_MULTIPART);
1482       obj.set_in_extra_data(true);
1483       res = get_obj_attrs(store, s, obj, xattrs);
1484       if (res == 0) {
1485         std::unique_ptr<BlockCrypt> block_crypt;
1486         /* We are adding to existing object.
1487          * We use crypto mode that configured as if we were decrypting. */
1488         res = rgw_s3_prepare_decrypt(s, xattrs, &block_crypt, crypt_http_responses);
1489         if (res == 0 && block_crypt != nullptr)
1490           *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
1491               new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
1492       }
1493     }
1494     /* it is ok, to not have encryption at all */
1495   }
1496   else
1497   {
1498     std::unique_ptr<BlockCrypt> block_crypt;
1499     res = rgw_s3_prepare_encrypt(s, attrs, nullptr, &block_crypt, crypt_http_responses);
1500     if (res == 0 && block_crypt != nullptr) {
1501       *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
1502           new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
1503     }
1504   }
1505   return res;
1506 }
1507
1508 void RGWPostObj_ObjStore_S3::rebuild_key(string& key)
1509 {
1510   static string var = "${filename}";
1511   int pos = key.find(var);
1512   if (pos < 0)
1513     return;
1514
1515   string new_key = key.substr(0, pos);
1516   new_key.append(filename);
1517   new_key.append(key.substr(pos + var.size()));
1518
1519   key = new_key;
1520 }
1521
1522 std::string RGWPostObj_ObjStore_S3::get_current_filename() const
1523 {
1524   return s->object.name;
1525 }
1526
1527 std::string RGWPostObj_ObjStore_S3::get_current_content_type() const
1528 {
1529   return content_type;
1530 }
1531
1532 int RGWPostObj_ObjStore_S3::get_params()
1533 {
1534   op_ret = RGWPostObj_ObjStore::get_params();
1535   if (op_ret < 0) {
1536     return op_ret;
1537   }
1538
1539   ldout(s->cct, 20) << "adding bucket to policy env: " << s->bucket.name
1540                     << dendl;
1541   env.add_var("bucket", s->bucket.name);
1542
1543   bool done;
1544   do {
1545     struct post_form_part part;
1546     int r = read_form_part_header(&part, done);
1547     if (r < 0)
1548       return r;
1549
1550     if (s->cct->_conf->subsys.should_gather(ceph_subsys_rgw, 20)) {
1551       ldout(s->cct, 20) << "read part header -- part.name="
1552                         << part.name << dendl;
1553
1554       for (const auto& pair : part.fields) {
1555         ldout(s->cct, 20) << "field.name=" << pair.first << dendl;
1556         ldout(s->cct, 20) << "field.val=" << pair.second.val << dendl;
1557         ldout(s->cct, 20) << "field.params:" << dendl;
1558
1559         for (const auto& param_pair : pair.second.params) {
1560           ldout(s->cct, 20) << " " << param_pair.first
1561                             << " -> " << param_pair.second << dendl;
1562         }
1563       }
1564     }
1565
1566     if (done) { /* unexpected here */
1567       err_msg = "Malformed request";
1568       return -EINVAL;
1569     }
1570
1571     if (stringcasecmp(part.name, "file") == 0) { /* beginning of data transfer */
1572       struct post_part_field& field = part.fields["Content-Disposition"];
1573       map<string, string>::iterator iter = field.params.find("filename");
1574       if (iter != field.params.end()) {
1575         filename = iter->second;
1576       }
1577       parts[part.name] = part;
1578       break;
1579     }
1580
1581     bool boundary;
1582     uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
1583     r = read_data(part.data, chunk_size, boundary, done);
1584     if (r < 0 || !boundary) {
1585       err_msg = "Couldn't find boundary";
1586       return -EINVAL;
1587     }
1588     parts[part.name] = part;
1589     string part_str(part.data.c_str(), part.data.length());
1590     env.add_var(part.name, part_str);
1591   } while (!done);
1592
1593   string object_str;
1594   if (!part_str(parts, "key", &object_str)) {
1595     err_msg = "Key not specified";
1596     return -EINVAL;
1597   }
1598
1599   s->object = rgw_obj_key(object_str);
1600
1601   rebuild_key(s->object.name);
1602
1603   if (s->object.empty()) {
1604     err_msg = "Empty object name";
1605     return -EINVAL;
1606   }
1607
1608   env.add_var("key", s->object.name);
1609
1610   part_str(parts, "Content-Type", &content_type);
1611   env.add_var("Content-Type", content_type);
1612
1613   map<string, struct post_form_part, ltstr_nocase>::iterator piter =
1614     parts.upper_bound(RGW_AMZ_META_PREFIX);
1615   for (; piter != parts.end(); ++piter) {
1616     string n = piter->first;
1617     if (strncasecmp(n.c_str(), RGW_AMZ_META_PREFIX,
1618                     sizeof(RGW_AMZ_META_PREFIX) - 1) != 0)
1619       break;
1620
1621     string attr_name = RGW_ATTR_PREFIX;
1622     attr_name.append(n);
1623
1624     /* need to null terminate it */
1625     bufferlist& data = piter->second.data;
1626     string str = string(data.c_str(), data.length());
1627
1628     bufferlist attr_bl;
1629     attr_bl.append(str.c_str(), str.size() + 1);
1630
1631     attrs[attr_name] = attr_bl;
1632   }
1633   // TODO: refactor this and the above loop to share code
1634   piter = parts.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION);
1635   if (piter != parts.end()) {
1636     string n = piter->first;
1637     string attr_name = RGW_ATTR_PREFIX;
1638     attr_name.append(n);
1639     /* need to null terminate it */
1640     bufferlist& data = piter->second.data;
1641     string str = string(data.c_str(), data.length());
1642
1643     bufferlist attr_bl;
1644     attr_bl.append(str.c_str(), str.size() + 1);
1645
1646     attrs[attr_name] = attr_bl;
1647   }
1648
1649   int r = get_policy();
1650   if (r < 0)
1651     return r;
1652
1653   r = get_tags();
1654   if (r < 0)
1655     return r;
1656
1657
1658   min_len = post_policy.min_length;
1659   max_len = post_policy.max_length;
1660
1661
1662
1663   return 0;
1664 }
1665
1666 int RGWPostObj_ObjStore_S3::get_tags()
1667 {
1668   string tags_str;
1669   if (part_str(parts, "tagging", &tags_str)) {
1670     RGWObjTagsXMLParser parser;
1671     if (!parser.init()){
1672       ldout(s->cct, 0) << "Couldn't init RGWObjTags XML parser" << dendl;
1673       err_msg = "Server couldn't process the request";
1674       return -EINVAL; // TODO: This class of errors in rgw code should be a 5XX error
1675     }
1676     if (!parser.parse(tags_str.c_str(), tags_str.size(), 1)) {
1677       ldout(s->cct,0 ) << "Invalid Tagging XML" << dendl;
1678       err_msg = "Invalid Tagging XML";
1679       return -EINVAL;
1680     }
1681
1682     RGWObjTagSet_S3 *obj_tags_s3;
1683     RGWObjTagging_S3 *tagging;
1684
1685     tagging = static_cast<RGWObjTagging_S3 *>(parser.find_first("Tagging"));
1686     obj_tags_s3 = static_cast<RGWObjTagSet_S3 *>(tagging->find_first("TagSet"));
1687     if(!obj_tags_s3){
1688       return -ERR_MALFORMED_XML;
1689     }
1690
1691     RGWObjTags obj_tags;
1692     int r = obj_tags_s3->rebuild(obj_tags);
1693     if (r < 0)
1694       return r;
1695
1696     bufferlist tags_bl;
1697     obj_tags.encode(tags_bl);
1698     ldout(s->cct, 20) << "Read " << obj_tags.count() << "tags" << dendl;
1699     attrs[RGW_ATTR_TAGS] = tags_bl;
1700   }
1701
1702
1703   return 0;
1704 }
1705
1706 int RGWPostObj_ObjStore_S3::get_policy()
1707 {
1708   if (part_bl(parts, "policy", &s->auth.s3_postobj_creds.encoded_policy)) {
1709     bool aws4_auth = false;
1710
1711     /* x-amz-algorithm handling */
1712     using rgw::auth::s3::AWS4_HMAC_SHA256_STR;
1713     if ((part_str(parts, "x-amz-algorithm", &s->auth.s3_postobj_creds.x_amz_algorithm)) &&
1714         (s->auth.s3_postobj_creds.x_amz_algorithm == AWS4_HMAC_SHA256_STR)) {
1715       ldout(s->cct, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl;
1716       aws4_auth = true;
1717     } else {
1718       ldout(s->cct, 0) << "Signature verification algorithm AWS v2" << dendl;
1719     }
1720
1721     // check that the signature matches the encoded policy
1722     if (aws4_auth) {
1723       /* AWS4 */
1724
1725       /* x-amz-credential handling */
1726       if (!part_str(parts, "x-amz-credential",
1727                     &s->auth.s3_postobj_creds.x_amz_credential)) {
1728         ldout(s->cct, 0) << "No S3 aws4 credential found!" << dendl;
1729         err_msg = "Missing aws4 credential";
1730         return -EINVAL;
1731       }
1732
1733       /* x-amz-signature handling */
1734       if (!part_str(parts, "x-amz-signature",
1735                     &s->auth.s3_postobj_creds.signature)) {
1736         ldout(s->cct, 0) << "No aws4 signature found!" << dendl;
1737         err_msg = "Missing aws4 signature";
1738         return -EINVAL;
1739       }
1740
1741       /* x-amz-date handling */
1742       std::string received_date_str;
1743       if (!part_str(parts, "x-amz-date", &received_date_str)) {
1744         ldout(s->cct, 0) << "No aws4 date found!" << dendl;
1745         err_msg = "Missing aws4 date";
1746         return -EINVAL;
1747       }
1748     } else {
1749       /* AWS2 */
1750
1751       // check that the signature matches the encoded policy
1752       if (!part_str(parts, "AWSAccessKeyId",
1753                     &s->auth.s3_postobj_creds.access_key)) {
1754         ldout(s->cct, 0) << "No S3 aws2 access key found!" << dendl;
1755         err_msg = "Missing aws2 access key";
1756         return -EINVAL;
1757       }
1758
1759       if (!part_str(parts, "signature", &s->auth.s3_postobj_creds.signature)) {
1760         ldout(s->cct, 0) << "No aws2 signature found!" << dendl;
1761         err_msg = "Missing aws2 signature";
1762         return -EINVAL;
1763       }
1764     }
1765
1766     /* FIXME: this is a makeshift solution. The browser upload authentication will be
1767      * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
1768      * method. */
1769     const int ret = rgw::auth::Strategy::apply(auth_registry_ptr->get_s3_post(), s);
1770     if (ret != 0) {
1771       return -EACCES;
1772     } else {
1773       /* Populate the owner info. */
1774       s->owner.set_id(s->user->user_id);
1775       s->owner.set_name(s->user->display_name);
1776       ldout(s->cct, 20) << "Successful Signature Verification!" << dendl;
1777     }
1778
1779     ceph::bufferlist decoded_policy;
1780     try {
1781       decoded_policy.decode_base64(s->auth.s3_postobj_creds.encoded_policy);
1782     } catch (buffer::error& err) {
1783       ldout(s->cct, 0) << "failed to decode_base64 policy" << dendl;
1784       err_msg = "Could not decode policy";
1785       return -EINVAL;
1786     }
1787
1788     decoded_policy.append('\0'); // NULL terminate
1789     ldout(s->cct, 20) << "POST policy: " << decoded_policy.c_str() << dendl;
1790
1791
1792     int r = post_policy.from_json(decoded_policy, err_msg);
1793     if (r < 0) {
1794       if (err_msg.empty()) {
1795         err_msg = "Failed to parse policy";
1796       }
1797       ldout(s->cct, 0) << "failed to parse policy" << dendl;
1798       return -EINVAL;
1799     }
1800
1801     if (aws4_auth) {
1802       /* AWS4 */
1803       post_policy.set_var_checked("x-amz-signature");
1804     } else {
1805       /* AWS2 */
1806       post_policy.set_var_checked("AWSAccessKeyId");
1807       post_policy.set_var_checked("signature");
1808     }
1809     post_policy.set_var_checked("policy");
1810
1811     r = post_policy.check(&env, err_msg);
1812     if (r < 0) {
1813       if (err_msg.empty()) {
1814         err_msg = "Policy check failed";
1815       }
1816       ldout(s->cct, 0) << "policy check failed" << dendl;
1817       return r;
1818     }
1819
1820   } else {
1821     ldout(s->cct, 0) << "No attached policy found!" << dendl;
1822   }
1823
1824   string canned_acl;
1825   part_str(parts, "acl", &canned_acl);
1826
1827   RGWAccessControlPolicy_S3 s3policy(s->cct);
1828   ldout(s->cct, 20) << "canned_acl=" << canned_acl << dendl;
1829   if (s3policy.create_canned(s->owner, s->bucket_owner, canned_acl) < 0) {
1830     err_msg = "Bad canned ACLs";
1831     return -EINVAL;
1832   }
1833
1834   policy = s3policy;
1835
1836   return 0;
1837 }
1838
1839 int RGWPostObj_ObjStore_S3::complete_get_params()
1840 {
1841   bool done;
1842   do {
1843     struct post_form_part part;
1844     int r = read_form_part_header(&part, done);
1845     if (r < 0) {
1846       return r;
1847     }
1848
1849     ceph::bufferlist part_data;
1850     bool boundary;
1851     uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
1852     r = read_data(part.data, chunk_size, boundary, done);
1853     if (r < 0 || !boundary) {
1854       return -EINVAL;
1855     }
1856
1857     /* Just reading the data but not storing any results of that. */
1858   } while (!done);
1859
1860   return 0;
1861 }
1862
1863 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist& bl, bool& again)
1864 {
1865   bool boundary;
1866   bool done;
1867
1868   const uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
1869   int r = read_data(bl, chunk_size, boundary, done);
1870   if (r < 0) {
1871     return r;
1872   }
1873
1874   if (boundary) {
1875     if (!done) {
1876       /* Reached end of data, let's drain the rest of the params */
1877       r = complete_get_params();
1878       if (r < 0) {
1879        return r;
1880       }
1881     }
1882   }
1883
1884   again = !boundary;
1885   return bl.length();
1886 }
1887
1888 void RGWPostObj_ObjStore_S3::send_response()
1889 {
1890   if (op_ret == 0 && parts.count("success_action_redirect")) {
1891     string redirect;
1892
1893     part_str(parts, "success_action_redirect", &redirect);
1894
1895     string tenant;
1896     string bucket;
1897     string key;
1898     string etag_str = "\"";
1899
1900     etag_str.append(etag);
1901     etag_str.append("\"");
1902
1903     string etag_url;
1904
1905     url_encode(s->bucket_tenant, tenant); /* surely overkill, but cheap */
1906     url_encode(s->bucket_name, bucket);
1907     url_encode(s->object.name, key);
1908     url_encode(etag_str, etag_url);
1909
1910     if (!s->bucket_tenant.empty()) {
1911       /*
1912        * What we really would like is to quaily the bucket name, so
1913        * that the client could simply copy it and paste into next request.
1914        * Unfortunately, in S3 we cannot know if the client will decide
1915        * to come through DNS, with "bucket.tenant" sytanx, or through
1916        * URL with "tenant\bucket" syntax. Therefore, we provide the
1917        * tenant separately.
1918        */
1919       redirect.append("?tenant=");
1920       redirect.append(tenant);
1921       redirect.append("&bucket=");
1922       redirect.append(bucket);
1923     } else {
1924       redirect.append("?bucket=");
1925       redirect.append(bucket);
1926     }
1927     redirect.append("&key=");
1928     redirect.append(key);
1929     redirect.append("&etag=");
1930     redirect.append(etag_url);
1931
1932     int r = check_utf8(redirect.c_str(), redirect.size());
1933     if (r < 0) {
1934       op_ret = r;
1935       goto done;
1936     }
1937     dump_redirect(s, redirect);
1938     op_ret = STATUS_REDIRECT;
1939   } else if (op_ret == 0 && parts.count("success_action_status")) {
1940     string status_string;
1941     uint32_t status_int;
1942
1943     part_str(parts, "success_action_status", &status_string);
1944
1945     int r = stringtoul(status_string, &status_int);
1946     if (r < 0) {
1947       op_ret = r;
1948       goto done;
1949     }
1950
1951     switch (status_int) {
1952       case 200:
1953         break;
1954       case 201:
1955         op_ret = STATUS_CREATED;
1956         break;
1957       default:
1958         op_ret = STATUS_NO_CONTENT;
1959         break;
1960     }
1961   } else if (! op_ret) {
1962     op_ret = STATUS_NO_CONTENT;
1963   }
1964
1965 done:
1966   if (op_ret == STATUS_CREATED) {
1967     for (auto &it : crypt_http_responses)
1968       dump_header(s, it.first, it.second);
1969     s->formatter->open_object_section("PostResponse");
1970     if (g_conf->rgw_dns_name.length())
1971       s->formatter->dump_format("Location", "%s/%s",
1972                                 s->info.script_uri.c_str(),
1973                                 s->object.name.c_str());
1974     if (!s->bucket_tenant.empty())
1975       s->formatter->dump_string("Tenant", s->bucket_tenant);
1976     s->formatter->dump_string("Bucket", s->bucket_name);
1977     s->formatter->dump_string("Key", s->object.name);
1978     s->formatter->close_section();
1979   }
1980   s->err.message = err_msg;
1981   set_req_state_err(s, op_ret);
1982   dump_errno(s);
1983   if (op_ret >= 0) {
1984     dump_content_length(s, s->formatter->get_len());
1985   }
1986   end_header(s, this);
1987   if (op_ret != STATUS_CREATED)
1988     return;
1989
1990   rgw_flush_formatter_and_reset(s, s->formatter);
1991 }
1992
1993 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
1994     std::unique_ptr<RGWPutObjDataProcessor>* filter, RGWPutObjDataProcessor* cb)
1995 {
1996   int res = 0;
1997   std::unique_ptr<BlockCrypt> block_crypt;
1998   res = rgw_s3_prepare_encrypt(s, attrs, &parts, &block_crypt, crypt_http_responses);
1999   if (res == 0 && block_crypt != nullptr) {
2000     *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
2001         new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
2002   }
2003   else
2004     *filter = nullptr;
2005   return res;
2006 }
2007
2008 int RGWDeleteObj_ObjStore_S3::get_params()
2009 {
2010   const char *if_unmod = s->info.env->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
2011
2012   if (s->system_request) {
2013     s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "no-precondition-error", &no_precondition_error, false);
2014   }
2015
2016   if (if_unmod) {
2017     std::string if_unmod_decoded = url_decode(if_unmod);
2018     uint64_t epoch;
2019     uint64_t nsec;
2020     if (utime_t::parse_date(if_unmod_decoded, &epoch, &nsec) < 0) {
2021       ldout(s->cct, 10) << "failed to parse time: " << if_unmod_decoded << dendl;
2022       return -EINVAL;
2023     }
2024     unmod_since = utime_t(epoch, nsec).to_real_time();
2025   }
2026
2027   return 0;
2028 }
2029
2030 void RGWDeleteObj_ObjStore_S3::send_response()
2031 {
2032   int r = op_ret;
2033   if (r == -ENOENT)
2034     r = 0;
2035   if (!r)
2036     r = STATUS_NO_CONTENT;
2037
2038   set_req_state_err(s, r);
2039   dump_errno(s);
2040   if (!version_id.empty()) {
2041     dump_header(s, "x-amz-version-id", version_id);
2042   }
2043   if (delete_marker) {
2044     dump_header(s, "x-amz-delete-marker", "true");
2045   }
2046   end_header(s, this);
2047 }
2048
2049 int RGWCopyObj_ObjStore_S3::init_dest_policy()
2050 {
2051   RGWAccessControlPolicy_S3 s3policy(s->cct);
2052
2053   /* build a policy for the target object */
2054   int r = create_s3_policy(s, store, s3policy, s->owner);
2055   if (r < 0)
2056     return r;
2057
2058   dest_policy = s3policy;
2059
2060   return 0;
2061 }
2062
2063 int RGWCopyObj_ObjStore_S3::get_params()
2064 {
2065   if_mod = s->info.env->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
2066   if_unmod = s->info.env->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
2067   if_match = s->info.env->get("HTTP_X_AMZ_COPY_IF_MATCH");
2068   if_nomatch = s->info.env->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
2069
2070   src_tenant_name = s->src_tenant_name;
2071   src_bucket_name = s->src_bucket_name;
2072   src_object = s->src_object;
2073   dest_tenant_name = s->bucket.tenant;
2074   dest_bucket_name = s->bucket.name;
2075   dest_object = s->object.name;
2076
2077   if (s->system_request) {
2078     source_zone = s->info.args.get(RGW_SYS_PARAM_PREFIX "source-zone");
2079     s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "copy-if-newer", &copy_if_newer, false);
2080     if (!source_zone.empty()) {
2081       client_id = s->info.args.get(RGW_SYS_PARAM_PREFIX "client-id");
2082       op_id = s->info.args.get(RGW_SYS_PARAM_PREFIX "op-id");
2083
2084       if (client_id.empty() || op_id.empty()) {
2085         ldout(s->cct, 0) <<
2086           RGW_SYS_PARAM_PREFIX "client-id or "
2087           RGW_SYS_PARAM_PREFIX "op-id were not provided, "
2088           "required for intra-region copy"
2089                          << dendl;
2090         return -EINVAL;
2091       }
2092     }
2093   }
2094
2095   const char *md_directive = s->info.env->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
2096   if (md_directive) {
2097     if (strcasecmp(md_directive, "COPY") == 0) {
2098       attrs_mod = RGWRados::ATTRSMOD_NONE;
2099     } else if (strcasecmp(md_directive, "REPLACE") == 0) {
2100       attrs_mod = RGWRados::ATTRSMOD_REPLACE;
2101     } else if (!source_zone.empty()) {
2102       attrs_mod = RGWRados::ATTRSMOD_NONE; // default for intra-zone_group copy
2103     } else {
2104       ldout(s->cct, 0) << "invalid metadata directive" << dendl;
2105       return -EINVAL;
2106     }
2107   }
2108
2109   if (source_zone.empty() &&
2110       (dest_tenant_name.compare(src_tenant_name) == 0) &&
2111       (dest_bucket_name.compare(src_bucket_name) == 0) &&
2112       (dest_object.compare(src_object.name) == 0) &&
2113       src_object.instance.empty() &&
2114       (attrs_mod != RGWRados::ATTRSMOD_REPLACE)) {
2115     /* can only copy object into itself if replacing attrs */
2116     ldout(s->cct, 0) << "can't copy object into itself if not replacing attrs"
2117                      << dendl;
2118     return -ERR_INVALID_REQUEST;
2119   }
2120   return 0;
2121 }
2122
2123 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs)
2124 {
2125   if (! sent_header) {
2126     if (op_ret)
2127     set_req_state_err(s, op_ret);
2128     dump_errno(s);
2129
2130     end_header(s, this, "application/xml");
2131     if (op_ret == 0) {
2132       s->formatter->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3);
2133     }
2134     sent_header = true;
2135   } else {
2136     /* Send progress field. Note that this diverge from the original S3
2137      * spec. We do this in order to keep connection alive.
2138      */
2139     s->formatter->dump_int("Progress", (uint64_t)ofs);
2140   }
2141   rgw_flush_formatter(s, s->formatter);
2142 }
2143
2144 void RGWCopyObj_ObjStore_S3::send_response()
2145 {
2146   if (!sent_header)
2147     send_partial_response(0);
2148
2149   if (op_ret == 0) {
2150     dump_time(s, "LastModified", &mtime);
2151     std::string etag_str = etag.to_str();
2152     if (! etag_str.empty()) {
2153       s->formatter->dump_string("ETag", std::move(etag_str));
2154     }
2155     s->formatter->close_section();
2156     rgw_flush_formatter_and_reset(s, s->formatter);
2157   }
2158 }
2159
2160 void RGWGetACLs_ObjStore_S3::send_response()
2161 {
2162   if (op_ret)
2163     set_req_state_err(s, op_ret);
2164   dump_errno(s);
2165   end_header(s, this, "application/xml");
2166   dump_start(s);
2167   rgw_flush_formatter(s, s->formatter);
2168   dump_body(s, acls);
2169 }
2170
2171 int RGWPutACLs_ObjStore_S3::get_params()
2172 {
2173   int ret =  RGWPutACLs_ObjStore::get_params();
2174   if (ret >= 0) {
2175     const int ret_auth = do_aws4_auth_completion();
2176     if (ret_auth < 0) {
2177       return ret_auth;
2178     }
2179   }
2180   return ret;
2181 }
2182
2183 int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados *store,
2184                                                   struct req_state *s,
2185                                                   stringstream& ss)
2186 {
2187   RGWAccessControlPolicy_S3 s3policy(s->cct);
2188
2189   // bucket-* canned acls do not apply to bucket
2190   if (s->object.empty()) {
2191     if (s->canned_acl.find("bucket") != string::npos)
2192       s->canned_acl.clear();
2193   }
2194
2195   int r = create_s3_policy(s, store, s3policy, owner);
2196   if (r < 0)
2197     return r;
2198
2199   s3policy.to_xml(ss);
2200
2201   return 0;
2202 }
2203
2204 void RGWPutACLs_ObjStore_S3::send_response()
2205 {
2206   if (op_ret)
2207     set_req_state_err(s, op_ret);
2208   dump_errno(s);
2209   end_header(s, this, "application/xml");
2210   dump_start(s);
2211 }
2212
2213 void RGWGetLC_ObjStore_S3::execute()
2214 {
2215   config.set_ctx(s->cct);
2216
2217   map<string, bufferlist>::iterator aiter = s->bucket_attrs.find(RGW_ATTR_LC);
2218   if (aiter == s->bucket_attrs.end()) {
2219     op_ret = -ENOENT;
2220     return;
2221   }
2222
2223   bufferlist::iterator iter(&aiter->second);
2224   try {
2225       config.decode(iter);
2226     } catch (const buffer::error& e) {
2227       ldout(s->cct, 0) << __func__ <<  "decode life cycle config failed" << dendl;
2228       op_ret = -EIO;
2229       return;
2230     }
2231 }
2232
2233 void RGWGetLC_ObjStore_S3::send_response()
2234 {
2235   if (op_ret) {
2236     if (op_ret == -ENOENT) {    
2237       set_req_state_err(s, ERR_NO_SUCH_LC);
2238     } else {
2239       set_req_state_err(s, op_ret);
2240     }
2241   }
2242   dump_errno(s);
2243   end_header(s, this, "application/xml");
2244   dump_start(s);
2245
2246   if (op_ret < 0)
2247     return;
2248
2249   config.dump_xml(s->formatter);
2250   rgw_flush_formatter_and_reset(s, s->formatter);
2251 }
2252
2253 void RGWPutLC_ObjStore_S3::send_response()
2254 {
2255   if (op_ret)
2256     set_req_state_err(s, op_ret);
2257   dump_errno(s);
2258   end_header(s, this, "application/xml");
2259   dump_start(s);
2260 }
2261
2262 void RGWDeleteLC_ObjStore_S3::send_response()
2263 {
2264   if (op_ret == 0)
2265       op_ret = STATUS_NO_CONTENT;
2266   if (op_ret) {   
2267     set_req_state_err(s, op_ret);
2268   }
2269   dump_errno(s);
2270   end_header(s, this, "application/xml");
2271   dump_start(s);
2272 }
2273
2274 void RGWGetCORS_ObjStore_S3::send_response()
2275 {
2276   if (op_ret) {
2277     if (op_ret == -ENOENT)
2278       set_req_state_err(s, ERR_NOT_FOUND);
2279     else
2280       set_req_state_err(s, op_ret);
2281   }
2282   dump_errno(s);
2283   end_header(s, NULL, "application/xml");
2284   dump_start(s);
2285   if (! op_ret) {
2286     string cors;
2287     RGWCORSConfiguration_S3 *s3cors =
2288       static_cast<RGWCORSConfiguration_S3 *>(&bucket_cors);
2289     stringstream ss;
2290
2291     s3cors->to_xml(ss);
2292     cors = ss.str();
2293     dump_body(s, cors);
2294   }
2295 }
2296
2297 int RGWPutCORS_ObjStore_S3::get_params()
2298 {
2299   int r;
2300   char *data = nullptr;
2301   int len = 0;
2302   RGWCORSXMLParser_S3 parser(s->cct);
2303   RGWCORSConfiguration_S3 *cors_config;
2304
2305   const auto max_size = s->cct->_conf->rgw_max_put_param_size;
2306   r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
2307   if (r < 0) {
2308     return r;
2309   }
2310
2311   auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
2312
2313   r = do_aws4_auth_completion();
2314   if (r < 0) {
2315     return r;
2316   }
2317
2318   if (!parser.init()) {
2319     return -EINVAL;
2320   }
2321
2322   if (!data || !parser.parse(data, len, 1)) {
2323     return -EINVAL;
2324   }
2325   cors_config =
2326     static_cast<RGWCORSConfiguration_S3 *>(parser.find_first(
2327                                              "CORSConfiguration"));
2328   if (!cors_config) {
2329     return -EINVAL;
2330   }
2331
2332   // forward bucket cors requests to meta master zone
2333   if (!store->is_meta_master()) {
2334     /* only need to keep this data around if we're not meta master */
2335     in_data.append(data, len);
2336   }
2337
2338   if (s->cct->_conf->subsys.should_gather(ceph_subsys_rgw, 15)) {
2339     ldout(s->cct, 15) << "CORSConfiguration";
2340     cors_config->to_xml(*_dout);
2341     *_dout << dendl;
2342   }
2343
2344   cors_config->encode(cors_bl);
2345
2346   return 0;
2347 }
2348
2349 void RGWPutCORS_ObjStore_S3::send_response()
2350 {
2351   if (op_ret)
2352     set_req_state_err(s, op_ret);
2353   dump_errno(s);
2354   end_header(s, NULL, "application/xml");
2355   dump_start(s);
2356 }
2357
2358 void RGWDeleteCORS_ObjStore_S3::send_response()
2359 {
2360   int r = op_ret;
2361   if (!r || r == -ENOENT)
2362     r = STATUS_NO_CONTENT;
2363
2364   set_req_state_err(s, r);
2365   dump_errno(s);
2366   end_header(s, NULL);
2367 }
2368
2369 void RGWOptionsCORS_ObjStore_S3::send_response()
2370 {
2371   string hdrs, exp_hdrs;
2372   uint32_t max_age = CORS_MAX_AGE_INVALID;
2373   /*EACCES means, there is no CORS registered yet for the bucket
2374    *ENOENT means, there is no match of the Origin in the list of CORSRule
2375    */
2376   if (op_ret == -ENOENT)
2377     op_ret = -EACCES;
2378   if (op_ret < 0) {
2379     set_req_state_err(s, op_ret);
2380     dump_errno(s);
2381     end_header(s, NULL);
2382     return;
2383   }
2384   get_response_params(hdrs, exp_hdrs, &max_age);
2385
2386   dump_errno(s);
2387   dump_access_control(s, origin, req_meth, hdrs.c_str(), exp_hdrs.c_str(),
2388                       max_age);
2389   end_header(s, NULL);
2390 }
2391
2392 void RGWGetRequestPayment_ObjStore_S3::send_response()
2393 {
2394   dump_errno(s);
2395   end_header(s, this, "application/xml");
2396   dump_start(s);
2397
2398   s->formatter->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3);
2399   const char *payer = requester_pays ? "Requester" :  "BucketOwner";
2400   s->formatter->dump_string("Payer", payer);
2401   s->formatter->close_section();
2402   rgw_flush_formatter_and_reset(s, s->formatter);
2403 }
2404
2405 class RGWSetRequestPaymentParser : public RGWXMLParser
2406 {
2407   XMLObj *alloc_obj(const char *el) override {
2408     return new XMLObj;
2409   }
2410
2411 public:
2412   RGWSetRequestPaymentParser() {}
2413   ~RGWSetRequestPaymentParser() override {}
2414
2415   int get_request_payment_payer(bool *requester_pays) {
2416     XMLObj *config = find_first("RequestPaymentConfiguration");
2417     if (!config)
2418       return -EINVAL;
2419
2420     *requester_pays = false;
2421
2422     XMLObj *field = config->find_first("Payer");
2423     if (!field)
2424       return 0;
2425
2426     string& s = field->get_data();
2427
2428     if (stringcasecmp(s, "Requester") == 0) {
2429       *requester_pays = true;
2430     } else if (stringcasecmp(s, "BucketOwner") != 0) {
2431       return -EINVAL;
2432     }
2433
2434     return 0;
2435   }
2436 };
2437
2438 int RGWSetRequestPayment_ObjStore_S3::get_params()
2439 {
2440   char *data;
2441   int len = 0;
2442   const auto max_size = s->cct->_conf->rgw_max_put_param_size;
2443   int r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
2444
2445   if (r < 0) {
2446     return r;
2447   }
2448
2449   RGWSetRequestPaymentParser parser;
2450
2451   if (!parser.init()) {
2452     ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
2453     r = -EIO;
2454     goto done;
2455   }
2456
2457   if (!parser.parse(data, len, 1)) {
2458     ldout(s->cct, 10) << "failed to parse data: " << data << dendl;
2459     r = -EINVAL;
2460     goto done;
2461   }
2462
2463   r = parser.get_request_payment_payer(&requester_pays);
2464
2465 done:
2466   free(data);
2467
2468   return r;
2469 }
2470
2471 void RGWSetRequestPayment_ObjStore_S3::send_response()
2472 {
2473   if (op_ret)
2474     set_req_state_err(s, op_ret);
2475   dump_errno(s);
2476   end_header(s);
2477 }
2478
2479 int RGWInitMultipart_ObjStore_S3::get_params()
2480 {
2481   RGWAccessControlPolicy_S3 s3policy(s->cct);
2482   op_ret = create_s3_policy(s, store, s3policy, s->owner);
2483   if (op_ret < 0)
2484     return op_ret;
2485
2486   policy = s3policy;
2487
2488   return 0;
2489 }
2490
2491 void RGWInitMultipart_ObjStore_S3::send_response()
2492 {
2493   if (op_ret)
2494     set_req_state_err(s, op_ret);
2495   dump_errno(s);
2496   for (auto &it : crypt_http_responses)
2497      dump_header(s, it.first, it.second);
2498   end_header(s, this, "application/xml");
2499   if (op_ret == 0) {
2500     dump_start(s);
2501     s->formatter->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3);
2502     if (!s->bucket_tenant.empty())
2503       s->formatter->dump_string("Tenant", s->bucket_tenant);
2504     s->formatter->dump_string("Bucket", s->bucket_name);
2505     s->formatter->dump_string("Key", s->object.name);
2506     s->formatter->dump_string("UploadId", upload_id);
2507     s->formatter->close_section();
2508     rgw_flush_formatter_and_reset(s, s->formatter);
2509   }
2510 }
2511
2512 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map<string, bufferlist>& attrs)
2513 {
2514   int res = 0;
2515   res = rgw_s3_prepare_encrypt(s, attrs, nullptr, nullptr, crypt_http_responses);
2516   return res;
2517 }
2518
2519 int RGWCompleteMultipart_ObjStore_S3::get_params()
2520 {
2521   int ret = RGWCompleteMultipart_ObjStore::get_params();
2522   if (ret < 0) {
2523     return ret;
2524   }
2525
2526   return do_aws4_auth_completion();
2527 }
2528
2529 void RGWCompleteMultipart_ObjStore_S3::send_response()
2530 {
2531   if (op_ret)
2532     set_req_state_err(s, op_ret);
2533   dump_errno(s);
2534   end_header(s, this, "application/xml");
2535   if (op_ret == 0) { 
2536     dump_start(s);
2537     s->formatter->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3);
2538     if (!s->bucket_tenant.empty()) {
2539       if (s->info.domain.length()) {
2540         s->formatter->dump_format("Location", "%s.%s.%s",
2541           s->bucket_name.c_str(),
2542           s->bucket_tenant.c_str(),
2543           s->info.domain.c_str());
2544       }
2545       s->formatter->dump_string("Tenant", s->bucket_tenant);
2546     } else {
2547       if (s->info.domain.length()) {
2548         s->formatter->dump_format("Location", "%s.%s",
2549           s->bucket_name.c_str(),
2550           s->info.domain.c_str());
2551       }
2552     }
2553     s->formatter->dump_string("Bucket", s->bucket_name);
2554     s->formatter->dump_string("Key", s->object.name);
2555     s->formatter->dump_string("ETag", etag);
2556     s->formatter->close_section();
2557     rgw_flush_formatter_and_reset(s, s->formatter);
2558   }
2559 }
2560
2561 void RGWAbortMultipart_ObjStore_S3::send_response()
2562 {
2563   int r = op_ret;
2564   if (!r)
2565     r = STATUS_NO_CONTENT;
2566
2567   set_req_state_err(s, r);
2568   dump_errno(s);
2569   end_header(s, this);
2570 }
2571
2572 void RGWListMultipart_ObjStore_S3::send_response()
2573 {
2574   if (op_ret)
2575     set_req_state_err(s, op_ret);
2576   dump_errno(s);
2577   end_header(s, this, "application/xml");
2578
2579   if (op_ret == 0) {
2580     dump_start(s);
2581     s->formatter->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3);
2582     map<uint32_t, RGWUploadPartInfo>::iterator iter;
2583     map<uint32_t, RGWUploadPartInfo>::reverse_iterator test_iter;
2584     int cur_max = 0;
2585
2586     iter = parts.begin();
2587     test_iter = parts.rbegin();
2588     if (test_iter != parts.rend()) {
2589       cur_max = test_iter->first;
2590     }
2591     if (!s->bucket_tenant.empty())
2592       s->formatter->dump_string("Tenant", s->bucket_tenant);
2593     s->formatter->dump_string("Bucket", s->bucket_name);
2594     s->formatter->dump_string("Key", s->object.name);
2595     s->formatter->dump_string("UploadId", upload_id);
2596     s->formatter->dump_string("StorageClass", "STANDARD");
2597     s->formatter->dump_int("PartNumberMarker", marker);
2598     s->formatter->dump_int("NextPartNumberMarker", cur_max);
2599     s->formatter->dump_int("MaxParts", max_parts);
2600     s->formatter->dump_string("IsTruncated", (truncated ? "true" : "false"));
2601
2602     ACLOwner& owner = policy.get_owner();
2603     dump_owner(s, owner.get_id(), owner.get_display_name());
2604
2605     for (; iter != parts.end(); ++iter) {
2606       RGWUploadPartInfo& info = iter->second;
2607
2608       s->formatter->open_object_section("Part");
2609
2610       dump_time(s, "LastModified", &info.modified);
2611
2612       s->formatter->dump_unsigned("PartNumber", info.num);
2613       s->formatter->dump_format("ETag", "\"%s\"", info.etag.c_str());
2614       s->formatter->dump_unsigned("Size", info.accounted_size);
2615       s->formatter->close_section();
2616     }
2617     s->formatter->close_section();
2618     rgw_flush_formatter_and_reset(s, s->formatter);
2619   }
2620 }
2621
2622 void RGWListBucketMultiparts_ObjStore_S3::send_response()
2623 {
2624   if (op_ret < 0)
2625     set_req_state_err(s, op_ret);
2626   dump_errno(s);
2627
2628   end_header(s, this, "application/xml");
2629   dump_start(s);
2630   if (op_ret < 0)
2631     return;
2632
2633   s->formatter->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3);
2634   if (!s->bucket_tenant.empty())
2635     s->formatter->dump_string("Tenant", s->bucket_tenant);
2636   s->formatter->dump_string("Bucket", s->bucket_name);
2637   if (!prefix.empty())
2638     s->formatter->dump_string("ListMultipartUploadsResult.Prefix", prefix);
2639   string& key_marker = marker.get_key();
2640   if (!key_marker.empty())
2641     s->formatter->dump_string("KeyMarker", key_marker);
2642   string& upload_id_marker = marker.get_upload_id();
2643   if (!upload_id_marker.empty())
2644     s->formatter->dump_string("UploadIdMarker", upload_id_marker);
2645   string next_key = next_marker.mp.get_key();
2646   if (!next_key.empty())
2647     s->formatter->dump_string("NextKeyMarker", next_key);
2648   string next_upload_id = next_marker.mp.get_upload_id();
2649   if (!next_upload_id.empty())
2650     s->formatter->dump_string("NextUploadIdMarker", next_upload_id);
2651   s->formatter->dump_int("MaxUploads", max_uploads);
2652   if (!delimiter.empty())
2653     s->formatter->dump_string("Delimiter", delimiter);
2654   s->formatter->dump_string("IsTruncated", (is_truncated ? "true" : "false"));
2655
2656   if (op_ret >= 0) {
2657     vector<RGWMultipartUploadEntry>::iterator iter;
2658     for (iter = uploads.begin(); iter != uploads.end(); ++iter) {
2659       RGWMPObj& mp = iter->mp;
2660       s->formatter->open_array_section("Upload");
2661       s->formatter->dump_string("Key", mp.get_key());
2662       s->formatter->dump_string("UploadId", mp.get_upload_id());
2663       dump_owner(s, s->user->user_id, s->user->display_name, "Initiator");
2664       dump_owner(s, s->user->user_id, s->user->display_name);
2665       s->formatter->dump_string("StorageClass", "STANDARD");
2666       dump_time(s, "Initiated", &iter->obj.meta.mtime);
2667       s->formatter->close_section();
2668     }
2669     if (!common_prefixes.empty()) {
2670       s->formatter->open_array_section("CommonPrefixes");
2671       map<string, bool>::iterator pref_iter;
2672       for (pref_iter = common_prefixes.begin();
2673            pref_iter != common_prefixes.end(); ++pref_iter) {
2674         s->formatter->dump_string("CommonPrefixes.Prefix", pref_iter->first);
2675       }
2676       s->formatter->close_section();
2677     }
2678   }
2679   s->formatter->close_section();
2680   rgw_flush_formatter_and_reset(s, s->formatter);
2681 }
2682
2683 int RGWDeleteMultiObj_ObjStore_S3::get_params()
2684 {
2685   int ret = RGWDeleteMultiObj_ObjStore::get_params();
2686   if (ret < 0) {
2687     return ret;
2688   }
2689
2690   return do_aws4_auth_completion();
2691 }
2692
2693 void RGWDeleteMultiObj_ObjStore_S3::send_status()
2694 {
2695   if (! status_dumped) {
2696     if (op_ret < 0)
2697       set_req_state_err(s, op_ret);
2698     dump_errno(s);
2699     status_dumped = true;
2700   }
2701 }
2702
2703 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
2704 {
2705
2706   if (!status_dumped) {
2707     send_status();
2708   }
2709
2710   dump_start(s);
2711   end_header(s, this, "application/xml");
2712   s->formatter->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3);
2713
2714   rgw_flush_formatter(s, s->formatter);
2715 }
2716
2717 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key& key,
2718                                                           bool delete_marker,
2719                                                           const string& marker_version_id, int ret)
2720 {
2721   if (!key.empty()) {
2722     if (op_ret == 0 && !quiet) {
2723       s->formatter->open_object_section("Deleted");
2724       s->formatter->dump_string("Key", key.name);
2725       if (!key.instance.empty()) {
2726         s->formatter->dump_string("VersionId", key.instance);
2727       }
2728       if (delete_marker) {
2729         s->formatter->dump_bool("DeleteMarker", true);
2730         s->formatter->dump_string("DeleteMarkerVersionId", marker_version_id);
2731       }
2732       s->formatter->close_section();
2733     } else if (op_ret < 0) {
2734       struct rgw_http_error r;
2735       int err_no;
2736
2737       s->formatter->open_object_section("Error");
2738
2739       err_no = -op_ret;
2740       rgw_get_errno_s3(&r, err_no);
2741
2742       s->formatter->dump_string("Key", key.name);
2743       s->formatter->dump_string("VersionId", key.instance);
2744       s->formatter->dump_int("Code", r.http_ret);
2745       s->formatter->dump_string("Message", r.s3_code);
2746       s->formatter->close_section();
2747     }
2748
2749     rgw_flush_formatter(s, s->formatter);
2750   }
2751 }
2752
2753 void RGWDeleteMultiObj_ObjStore_S3::end_response()
2754 {
2755
2756   s->formatter->close_section();
2757   rgw_flush_formatter_and_reset(s, s->formatter);
2758 }
2759
2760 void RGWGetObjLayout_ObjStore_S3::send_response()
2761 {
2762   if (op_ret)
2763     set_req_state_err(s, op_ret);
2764   dump_errno(s);
2765   end_header(s, this, "application/json");
2766
2767   JSONFormatter f;
2768
2769   if (op_ret < 0) {
2770     return;
2771   }
2772
2773   f.open_object_section("result");
2774   ::encode_json("head", head_obj, &f);
2775   ::encode_json("manifest", *manifest, &f);
2776   f.open_array_section("data_location");
2777   for (auto miter = manifest->obj_begin(); miter != manifest->obj_end(); ++miter) {
2778     f.open_object_section("obj");
2779     rgw_raw_obj raw_loc = miter.get_location().get_raw_obj(store);
2780     ::encode_json("ofs", miter.get_ofs(), &f);
2781     ::encode_json("loc", raw_loc, &f);
2782     ::encode_json("loc_ofs", miter.location_ofs(), &f);
2783     ::encode_json("loc_size", miter.get_stripe_size(), &f);
2784     f.close_section();
2785     rgw_flush_formatter(s, &f);
2786   }
2787   f.close_section();
2788   f.close_section();
2789   rgw_flush_formatter(s, &f);
2790 }
2791
2792 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
2793 {
2794   auto iter = s->info.x_meta_map.find("x-amz-meta-search");
2795   if (iter == s->info.x_meta_map.end()) {
2796     s->err.message = "X-Rgw-Meta-Search header not provided";
2797     ldout(s->cct, 5) << s->err.message << dendl;
2798     return -EINVAL;
2799   }
2800
2801   list<string> expressions;
2802   get_str_list(iter->second, ",", expressions);
2803
2804   for (auto& expression : expressions) {
2805     vector<string> args;
2806     get_str_vec(expression, ";", args);
2807
2808     if (args.empty()) {
2809       s->err.message = "invalid empty expression";
2810       ldout(s->cct, 5) << s->err.message << dendl;
2811       return -EINVAL;
2812     }
2813     if (args.size() > 2) {
2814       s->err.message = string("invalid expression: ") + expression;
2815       ldout(s->cct, 5) << s->err.message << dendl;
2816       return -EINVAL;
2817     }
2818
2819     string key = boost::algorithm::to_lower_copy(rgw_trim_whitespace(args[0]));
2820     string val;
2821     if (args.size() > 1) {
2822       val = boost::algorithm::to_lower_copy(rgw_trim_whitespace(args[1]));
2823     }
2824
2825     if (!boost::algorithm::starts_with(key, RGW_AMZ_META_PREFIX)) {
2826       s->err.message = string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX "' : ") + expression;
2827       ldout(s->cct, 5) << s->err.message << dendl;
2828       return -EINVAL;
2829     }
2830
2831     key = key.substr(sizeof(RGW_AMZ_META_PREFIX) - 1);
2832
2833     ESEntityTypeMap::EntityType entity_type;
2834
2835     if (val.empty() || val == "str" || val == "string") {
2836       entity_type = ESEntityTypeMap::ES_ENTITY_STR;
2837     } else if (val == "int" || val == "integer") {
2838       entity_type = ESEntityTypeMap::ES_ENTITY_INT;
2839     } else if (val == "date" || val == "datetime") {
2840       entity_type = ESEntityTypeMap::ES_ENTITY_DATE;
2841     } else {
2842       s->err.message = string("invalid entity type: ") + val;
2843       ldout(s->cct, 5) << s->err.message << dendl;
2844       return -EINVAL;
2845     }
2846
2847     mdsearch_config[key] = entity_type;
2848   }
2849
2850   return 0;
2851 }
2852
2853 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
2854 {
2855   if (op_ret)
2856     set_req_state_err(s, op_ret);
2857   dump_errno(s);
2858   end_header(s, this);
2859 }
2860
2861 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
2862 {
2863   if (op_ret)
2864     set_req_state_err(s, op_ret);
2865   dump_errno(s);
2866   end_header(s, NULL, "application/xml");
2867
2868   Formatter *f = s->formatter;
2869   f->open_array_section("GetBucketMetaSearchResult");
2870   for (auto& e : s->bucket_info.mdsearch_config) {
2871     f->open_object_section("Entry");
2872     string k = string("x-amz-meta-") + e.first;
2873     f->dump_string("Key", k.c_str());
2874     const char *type;
2875     switch (e.second) {
2876       case ESEntityTypeMap::ES_ENTITY_INT:
2877         type = "int";
2878         break;
2879       case ESEntityTypeMap::ES_ENTITY_DATE:
2880         type = "date";
2881         break;
2882       default:
2883         type = "str";
2884     }
2885     f->dump_string("Type", type);
2886     f->close_section();
2887   }
2888   f->close_section();
2889   rgw_flush_formatter(s, f);
2890 }
2891
2892 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
2893 {
2894   if (op_ret)
2895     set_req_state_err(s, op_ret);
2896   dump_errno(s);
2897   end_header(s, this);
2898 }
2899
2900
2901 RGWOp *RGWHandler_REST_Service_S3::op_get()
2902 {
2903   if (is_usage_op()) {
2904     return new RGWGetUsage_ObjStore_S3;
2905   } else {
2906     return new RGWListBuckets_ObjStore_S3;
2907   }
2908 }
2909
2910 RGWOp *RGWHandler_REST_Service_S3::op_head()
2911 {
2912   return new RGWListBuckets_ObjStore_S3;
2913 }
2914
2915 RGWOp *RGWHandler_REST_Service_S3::op_post()
2916 {
2917   if (s->info.args.exists("Action")) {
2918     string action = s->info.args.get("Action");
2919     if (action.compare("CreateRole") == 0)
2920       return new RGWCreateRole;
2921     if (action.compare("DeleteRole") == 0)
2922       return new RGWDeleteRole;
2923     if (action.compare("GetRole") == 0)
2924       return new RGWGetRole;
2925     if (action.compare("UpdateAssumeRolePolicy") == 0)
2926       return new RGWModifyRole;
2927     if (action.compare("ListRoles") == 0)
2928       return new RGWListRoles;
2929     if (action.compare("PutRolePolicy") == 0)
2930       return new RGWPutRolePolicy;
2931     if (action.compare("GetRolePolicy") == 0)
2932       return new RGWGetRolePolicy;
2933     if (action.compare("ListRolePolicies") == 0)
2934       return new RGWListRolePolicies;
2935     if (action.compare("DeleteRolePolicy") == 0)
2936       return new RGWDeleteRolePolicy;
2937   }
2938   return NULL;
2939 }
2940
2941 RGWOp *RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data)
2942 {
2943   // Non-website mode
2944   if (get_data) {
2945     return new RGWListBucket_ObjStore_S3;
2946   } else {
2947     return new RGWStatBucket_ObjStore_S3;
2948   }
2949 }
2950
2951 RGWOp *RGWHandler_REST_Bucket_S3::op_get()
2952 {
2953   if (s->info.args.sub_resource_exists("logging"))
2954     return new RGWGetBucketLogging_ObjStore_S3;
2955
2956   if (s->info.args.sub_resource_exists("location"))
2957     return new RGWGetBucketLocation_ObjStore_S3;
2958
2959   if (s->info.args.sub_resource_exists("versioning"))
2960     return new RGWGetBucketVersioning_ObjStore_S3;
2961
2962   if (s->info.args.sub_resource_exists("website")) {
2963     if (!s->cct->_conf->rgw_enable_static_website) {
2964       return NULL;
2965     }
2966     return new RGWGetBucketWebsite_ObjStore_S3;
2967   }
2968
2969   if (s->info.args.exists("mdsearch")) {
2970     return new RGWGetBucketMetaSearch_ObjStore_S3;
2971   }
2972
2973   if (is_acl_op()) {
2974     return new RGWGetACLs_ObjStore_S3;
2975   } else if (is_cors_op()) {
2976     return new RGWGetCORS_ObjStore_S3;
2977   } else if (is_request_payment_op()) {
2978     return new RGWGetRequestPayment_ObjStore_S3;
2979   } else if (s->info.args.exists("uploads")) {
2980     return new RGWListBucketMultiparts_ObjStore_S3;
2981   } else if(is_lc_op()) {
2982     return new RGWGetLC_ObjStore_S3;
2983   } else if(is_policy_op()) {
2984     return new RGWGetBucketPolicy;
2985   }
2986   return get_obj_op(true);
2987 }
2988
2989 RGWOp *RGWHandler_REST_Bucket_S3::op_head()
2990 {
2991   if (is_acl_op()) {
2992     return new RGWGetACLs_ObjStore_S3;
2993   } else if (s->info.args.exists("uploads")) {
2994     return new RGWListBucketMultiparts_ObjStore_S3;
2995   }
2996   return get_obj_op(false);
2997 }
2998
2999 RGWOp *RGWHandler_REST_Bucket_S3::op_put()
3000 {
3001   if (s->info.args.sub_resource_exists("logging"))
3002     return NULL;
3003   if (s->info.args.sub_resource_exists("versioning"))
3004     return new RGWSetBucketVersioning_ObjStore_S3;
3005   if (s->info.args.sub_resource_exists("website")) {
3006     if (!s->cct->_conf->rgw_enable_static_website) {
3007       return NULL;
3008     }
3009     return new RGWSetBucketWebsite_ObjStore_S3;
3010   }
3011   if (is_acl_op()) {
3012     return new RGWPutACLs_ObjStore_S3;
3013   } else if (is_cors_op()) {
3014     return new RGWPutCORS_ObjStore_S3;
3015   } else if (is_request_payment_op()) {
3016     return new RGWSetRequestPayment_ObjStore_S3;
3017   } else if(is_lc_op()) {
3018     return new RGWPutLC_ObjStore_S3;
3019   } else if(is_policy_op()) {
3020     return new RGWPutBucketPolicy;
3021   }
3022   return new RGWCreateBucket_ObjStore_S3;
3023 }
3024
3025 RGWOp *RGWHandler_REST_Bucket_S3::op_delete()
3026 {
3027   if (is_cors_op()) {
3028     return new RGWDeleteCORS_ObjStore_S3;
3029   } else if(is_lc_op()) {
3030     return new RGWDeleteLC_ObjStore_S3;
3031   } else if(is_policy_op()) {
3032     return new RGWDeleteBucketPolicy;
3033   }
3034
3035   if (s->info.args.sub_resource_exists("website")) {
3036     if (!s->cct->_conf->rgw_enable_static_website) {
3037       return NULL;
3038     }
3039     return new RGWDeleteBucketWebsite_ObjStore_S3;
3040   }
3041
3042   if (s->info.args.exists("mdsearch")) {
3043     return new RGWDelBucketMetaSearch_ObjStore_S3;
3044   }
3045
3046   return new RGWDeleteBucket_ObjStore_S3;
3047 }
3048
3049 RGWOp *RGWHandler_REST_Bucket_S3::op_post()
3050 {
3051   if (s->info.args.exists("delete")) {
3052     return new RGWDeleteMultiObj_ObjStore_S3;
3053   }
3054
3055   if (s->info.args.exists("mdsearch")) {
3056     return new RGWConfigBucketMetaSearch_ObjStore_S3;
3057   }
3058
3059   return new RGWPostObj_ObjStore_S3;
3060 }
3061
3062 RGWOp *RGWHandler_REST_Bucket_S3::op_options()
3063 {
3064   return new RGWOptionsCORS_ObjStore_S3;
3065 }
3066
3067 RGWOp *RGWHandler_REST_Obj_S3::get_obj_op(bool get_data)
3068 {
3069   if (is_acl_op()) {
3070     return new RGWGetACLs_ObjStore_S3;
3071   }
3072   RGWGetObj_ObjStore_S3 *get_obj_op = new RGWGetObj_ObjStore_S3;
3073   get_obj_op->set_get_data(get_data);
3074   return get_obj_op;
3075 }
3076
3077 RGWOp *RGWHandler_REST_Obj_S3::op_get()
3078 {
3079   if (is_acl_op()) {
3080     return new RGWGetACLs_ObjStore_S3;
3081   } else if (s->info.args.exists("uploadId")) {
3082     return new RGWListMultipart_ObjStore_S3;
3083   } else if (s->info.args.exists("layout")) {
3084     return new RGWGetObjLayout_ObjStore_S3;
3085   } else if (is_tagging_op()) {
3086     return new RGWGetObjTags_ObjStore_S3;
3087   }
3088   return get_obj_op(true);
3089 }
3090
3091 RGWOp *RGWHandler_REST_Obj_S3::op_head()
3092 {
3093   if (is_acl_op()) {
3094     return new RGWGetACLs_ObjStore_S3;
3095   } else if (s->info.args.exists("uploadId")) {
3096     return new RGWListMultipart_ObjStore_S3;
3097   }
3098   return get_obj_op(false);
3099 }
3100
3101 RGWOp *RGWHandler_REST_Obj_S3::op_put()
3102 {
3103   if (is_acl_op()) {
3104     return new RGWPutACLs_ObjStore_S3;
3105   } else if (is_tagging_op()) {
3106     return new RGWPutObjTags_ObjStore_S3;
3107   }
3108
3109   if (s->init_state.src_bucket.empty())
3110     return new RGWPutObj_ObjStore_S3;
3111   else
3112     return new RGWCopyObj_ObjStore_S3;
3113 }
3114
3115 RGWOp *RGWHandler_REST_Obj_S3::op_delete()
3116 {
3117   if (is_tagging_op()) {
3118     return new RGWDeleteObjTags_ObjStore_S3;
3119   }
3120   string upload_id = s->info.args.get("uploadId");
3121
3122   if (upload_id.empty())
3123     return new RGWDeleteObj_ObjStore_S3;
3124   else
3125     return new RGWAbortMultipart_ObjStore_S3;
3126 }
3127
3128 RGWOp *RGWHandler_REST_Obj_S3::op_post()
3129 {
3130   if (s->info.args.exists("uploadId"))
3131     return new RGWCompleteMultipart_ObjStore_S3;
3132
3133   if (s->info.args.exists("uploads"))
3134     return new RGWInitMultipart_ObjStore_S3;
3135
3136   return new RGWPostObj_ObjStore_S3;
3137 }
3138
3139 RGWOp *RGWHandler_REST_Obj_S3::op_options()
3140 {
3141   return new RGWOptionsCORS_ObjStore_S3;
3142 }
3143
3144 int RGWHandler_REST_S3::init_from_header(struct req_state* s,
3145                                         int default_formatter,
3146                                         bool configurable_format)
3147 {
3148   string req;
3149   string first;
3150
3151   const char *req_name = s->relative_uri.c_str();
3152   const char *p;
3153
3154   if (*req_name == '?') {
3155     p = req_name;
3156   } else {
3157     p = s->info.request_params.c_str();
3158   }
3159
3160   s->info.args.set(p);
3161   s->info.args.parse();
3162
3163   /* must be called after the args parsing */
3164   int ret = allocate_formatter(s, default_formatter, configurable_format);
3165   if (ret < 0)
3166     return ret;
3167
3168   if (*req_name != '/')
3169     return 0;
3170
3171   req_name++;
3172
3173   if (!*req_name)
3174     return 0;
3175
3176   req = req_name;
3177   int pos = req.find('/');
3178   if (pos >= 0) {
3179     first = req.substr(0, pos);
3180   } else {
3181     first = req;
3182   }
3183
3184   /*
3185    * XXX The intent of the check for empty is apparently to let the bucket
3186    * name from DNS to be set ahead. However, we currently take the DNS
3187    * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
3188    * So, this check is meaningless.
3189    *
3190    * Rather than dropping this, the code needs to be changed into putting
3191    * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
3192    * into req_status.bucket_name directly.
3193    */
3194   if (s->init_state.url_bucket.empty()) {
3195     // Save bucket to tide us over until token is parsed.
3196     s->init_state.url_bucket = first;
3197     if (pos >= 0) {
3198       string encoded_obj_str = req.substr(pos+1);
3199       s->object = rgw_obj_key(encoded_obj_str, s->info.args.get("versionId"));
3200     }
3201   } else {
3202     s->object = rgw_obj_key(req_name, s->info.args.get("versionId"));
3203   }
3204   return 0;
3205 }
3206
3207 int RGWHandler_REST_S3::postauth_init()
3208 {
3209   struct req_init_state *t = &s->init_state;
3210   bool relaxed_names = s->cct->_conf->rgw_relaxed_s3_bucket_names;
3211
3212   rgw_parse_url_bucket(t->url_bucket, s->user->user_id.tenant,
3213                       s->bucket_tenant, s->bucket_name);
3214
3215   dout(10) << "s->object=" << (!s->object.empty() ? s->object : rgw_obj_key("<NULL>"))
3216            << " s->bucket=" << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name) << dendl;
3217
3218   int ret;
3219   ret = rgw_validate_tenant_name(s->bucket_tenant);
3220   if (ret)
3221     return ret;
3222   if (!s->bucket_name.empty()) {
3223     ret = valid_s3_bucket_name(s->bucket_name, relaxed_names);
3224     if (ret)
3225       return ret;
3226     ret = validate_object_name(s->object.name);
3227     if (ret)
3228       return ret;
3229   }
3230
3231   if (!t->src_bucket.empty()) {
3232     rgw_parse_url_bucket(t->src_bucket, s->user->user_id.tenant,
3233                         s->src_tenant_name, s->src_bucket_name);
3234     ret = rgw_validate_tenant_name(s->src_tenant_name);
3235     if (ret)
3236       return ret;
3237     ret = valid_s3_bucket_name(s->src_bucket_name, relaxed_names);
3238     if (ret)
3239       return ret;
3240   }
3241   return 0;
3242 }
3243
3244 int RGWHandler_REST_S3::init(RGWRados *store, struct req_state *s,
3245                              rgw::io::BasicClient *cio)
3246 {
3247   int ret;
3248
3249   s->dialect = "s3";
3250
3251   ret = rgw_validate_tenant_name(s->bucket_tenant);
3252   if (ret)
3253     return ret;
3254   bool relaxed_names = s->cct->_conf->rgw_relaxed_s3_bucket_names;
3255   if (!s->bucket_name.empty()) {
3256     ret = valid_s3_bucket_name(s->bucket_name, relaxed_names);
3257     if (ret)
3258       return ret;
3259     ret = validate_object_name(s->object.name);
3260     if (ret)
3261       return ret;
3262   }
3263
3264   const char *cacl = s->info.env->get("HTTP_X_AMZ_ACL");
3265   if (cacl)
3266     s->canned_acl = cacl;
3267
3268   s->has_acl_header = s->info.env->exists_prefix("HTTP_X_AMZ_GRANT");
3269
3270   const char *copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
3271
3272   if (copy_source && !s->info.env->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) {
3273     ret = RGWCopyObj::parse_copy_location(copy_source,
3274                                           s->init_state.src_bucket,
3275                                           s->src_object);
3276     if (!ret) {
3277       ldout(s->cct, 0) << "failed to parse copy location" << dendl;
3278       return -EINVAL; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
3279     }
3280   }
3281
3282   return RGWHandler_REST::init(store, s, cio);
3283 }
3284
3285 enum class AwsVersion {
3286   UNKOWN,
3287   V2,
3288   V4
3289 };
3290
3291 enum class AwsRoute {
3292   UNKOWN,
3293   QUERY_STRING,
3294   HEADERS
3295 };
3296
3297 static inline std::pair<AwsVersion, AwsRoute>
3298 discover_aws_flavour(const req_info& info)
3299 {
3300   using rgw::auth::s3::AWS4_HMAC_SHA256_STR;
3301
3302   AwsVersion version = AwsVersion::UNKOWN;
3303   AwsRoute route = AwsRoute::UNKOWN;
3304
3305   const char* http_auth = info.env->get("HTTP_AUTHORIZATION");
3306   if (http_auth && http_auth[0]) {
3307     /* Authorization in Header */
3308     route = AwsRoute::HEADERS;
3309
3310     if (!strncmp(http_auth, AWS4_HMAC_SHA256_STR,
3311                  strlen(AWS4_HMAC_SHA256_STR))) {
3312       /* AWS v4 */
3313       version = AwsVersion::V4;
3314     } else if (!strncmp(http_auth, "AWS ", 4)) {
3315       /* AWS v2 */
3316       version = AwsVersion::V2;
3317     }
3318   } else {
3319     route = AwsRoute::QUERY_STRING;
3320
3321     if (info.args.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR) {
3322       /* AWS v4 */
3323       version = AwsVersion::V4;
3324     } else if (!info.args.get("AWSAccessKeyId").empty()) {
3325       /* AWS v2 */
3326       version = AwsVersion::V2;
3327     }
3328   }
3329
3330   return std::make_pair(version, route);
3331 }
3332
3333 static void init_anon_user(struct req_state *s)
3334 {
3335   rgw_get_anon_user(*(s->user));
3336   s->perm_mask = RGW_PERM_FULL_CONTROL;
3337 }
3338
3339 /*
3340  * verify that a signed request comes from the keyholder
3341  * by checking the signature against our locally-computed version
3342  *
3343  * it tries AWS v4 before AWS v2
3344  */
3345 int RGW_Auth_S3::authorize(RGWRados* const store,
3346                            const rgw::auth::StrategyRegistry& auth_registry,
3347                            struct req_state* const s)
3348 {
3349
3350   /* neither keystone and rados enabled; warn and exit! */
3351   if (!store->ctx()->_conf->rgw_s3_auth_use_rados &&
3352       !store->ctx()->_conf->rgw_s3_auth_use_keystone &&
3353       !store->ctx()->_conf->rgw_s3_auth_use_ldap) {
3354     dout(0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl;
3355     return -EPERM;
3356   }
3357
3358   const auto ret = rgw::auth::Strategy::apply(auth_registry.get_s3_main(), s);
3359   if (ret == 0) {
3360     /* Populate the owner info. */
3361     s->owner.set_id(s->user->user_id);
3362     s->owner.set_name(s->user->display_name);
3363   }
3364   return ret;
3365 }
3366
3367 int RGWHandler_Auth_S3::init(RGWRados *store, struct req_state *state,
3368                              rgw::io::BasicClient *cio)
3369 {
3370   int ret = RGWHandler_REST_S3::init_from_header(state, RGW_FORMAT_JSON,
3371                                                      true);
3372   if (ret < 0)
3373     return ret;
3374
3375   return RGWHandler_REST::init(store, state, cio);
3376 }
3377
3378 RGWHandler_REST* RGWRESTMgr_S3::get_handler(struct req_state* const s,
3379                                             const rgw::auth::StrategyRegistry& auth_registry,
3380                                             const std::string& frontend_prefix)
3381 {
3382   bool is_s3website = enable_s3website && (s->prot_flags & RGW_REST_WEBSITE);
3383   int ret =
3384     RGWHandler_REST_S3::init_from_header(s,
3385                                         is_s3website ? RGW_FORMAT_HTML :
3386                                         RGW_FORMAT_XML, true);
3387   if (ret < 0)
3388     return NULL;
3389
3390   RGWHandler_REST* handler;
3391   // TODO: Make this more readable
3392   if (is_s3website) {
3393     if (s->init_state.url_bucket.empty()) {
3394       handler = new RGWHandler_REST_Service_S3Website(auth_registry);
3395     } else if (s->object.empty()) {
3396       handler = new RGWHandler_REST_Bucket_S3Website(auth_registry);
3397     } else {
3398       handler = new RGWHandler_REST_Obj_S3Website(auth_registry);
3399     }
3400   } else {
3401     if (s->init_state.url_bucket.empty()) {
3402       handler = new RGWHandler_REST_Service_S3(auth_registry);
3403     } else if (s->object.empty()) {
3404       handler = new RGWHandler_REST_Bucket_S3(auth_registry);
3405     } else {
3406       handler = new RGWHandler_REST_Obj_S3(auth_registry);
3407     }
3408   }
3409
3410   ldout(s->cct, 20) << __func__ << " handler=" << typeid(*handler).name()
3411                     << dendl;
3412   return handler;
3413 }
3414
3415 bool RGWHandler_REST_S3Website::web_dir() const {
3416   std::string subdir_name = url_decode(s->object.name);
3417
3418   if (subdir_name.empty()) {
3419     return false;
3420   } else if (subdir_name.back() == '/') {
3421     subdir_name.pop_back();
3422   }
3423
3424   rgw_obj obj(s->bucket, subdir_name);
3425
3426   RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
3427   obj_ctx.obj.set_atomic(obj);
3428   obj_ctx.obj.set_prefetch_data(obj);
3429
3430   RGWObjState* state = nullptr;
3431   if (store->get_obj_state(&obj_ctx, s->bucket_info, obj, &state, false) < 0) {
3432     return false;
3433   }
3434   if (! state->exists) {
3435     return false;
3436   }
3437   return state->exists;
3438 }
3439
3440 int RGWHandler_REST_S3Website::retarget(RGWOp* op, RGWOp** new_op) {
3441   *new_op = op;
3442   ldout(s->cct, 10) << __func__ << "Starting retarget" << dendl;
3443
3444   if (!(s->prot_flags & RGW_REST_WEBSITE))
3445     return 0;
3446
3447   RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
3448   int ret = store->get_bucket_info(obj_ctx, s->bucket_tenant,
3449                                   s->bucket_name, s->bucket_info, NULL,
3450                                   &s->bucket_attrs);
3451   if (ret < 0) {
3452       // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
3453       return -ERR_NO_SUCH_BUCKET;
3454   }
3455   if (!s->bucket_info.has_website) {
3456       // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
3457       return -ERR_NO_SUCH_WEBSITE_CONFIGURATION;
3458   }
3459
3460   rgw_obj_key new_obj;
3461   s->bucket_info.website_conf.get_effective_key(s->object.name, &new_obj.name, web_dir());
3462   ldout(s->cct, 10) << "retarget get_effective_key " << s->object << " -> "
3463                     << new_obj << dendl;
3464
3465   RGWBWRoutingRule rrule;
3466   bool should_redirect =
3467     s->bucket_info.website_conf.should_redirect(new_obj.name, 0, &rrule);
3468
3469   if (should_redirect) {
3470     const string& hostname = s->info.env->get("HTTP_HOST", "");
3471     const string& protocol =
3472       (s->info.env->get("SERVER_PORT_SECURE") ? "https" : "http");
3473     int redirect_code = 0;
3474     rrule.apply_rule(protocol, hostname, s->object.name, &s->redirect,
3475                     &redirect_code);
3476     // APply a custom HTTP response code
3477     if (redirect_code > 0)
3478       s->err.http_ret = redirect_code; // Apply a custom HTTP response code
3479     ldout(s->cct, 10) << "retarget redirect code=" << redirect_code
3480                       << " proto+host:" << protocol << "://" << hostname
3481                       << " -> " << s->redirect << dendl;
3482     return -ERR_WEBSITE_REDIRECT;
3483   }
3484
3485   /*
3486    * FIXME: if s->object != new_obj, drop op and create a new op to handle
3487    * operation. Or remove this comment if it's not applicable anymore
3488    */
3489
3490   s->object = new_obj;
3491
3492   return 0;
3493 }
3494
3495 RGWOp* RGWHandler_REST_S3Website::op_get()
3496 {
3497   return get_obj_op(true);
3498 }
3499
3500 RGWOp* RGWHandler_REST_S3Website::op_head()
3501 {
3502   return get_obj_op(false);
3503 }
3504
3505 int RGWHandler_REST_S3Website::serve_errordoc(int http_ret, const string& errordoc_key) {
3506   int ret = 0;
3507   s->formatter->reset(); /* Try to throw it all away */
3508
3509   std::shared_ptr<RGWGetObj_ObjStore_S3Website> getop( static_cast<RGWGetObj_ObjStore_S3Website*>(op_get()));
3510   if (getop.get() == NULL) {
3511     return -1; // Trigger double error handler
3512   }
3513   getop->init(store, s, this);
3514   getop->range_str = NULL;
3515   getop->if_mod = NULL;
3516   getop->if_unmod = NULL;
3517   getop->if_match = NULL;
3518   getop->if_nomatch = NULL;
3519   s->object = errordoc_key;
3520
3521   ret = init_permissions(getop.get());
3522   if (ret < 0) {
3523     ldout(s->cct, 20) << "serve_errordoc failed, init_permissions ret=" << ret << dendl;
3524     return -1; // Trigger double error handler
3525   }
3526
3527   ret = read_permissions(getop.get());
3528   if (ret < 0) {
3529     ldout(s->cct, 20) << "serve_errordoc failed, read_permissions ret=" << ret << dendl;
3530     return -1; // Trigger double error handler
3531   }
3532
3533   if (http_ret) {
3534      getop->set_custom_http_response(http_ret);
3535   }
3536
3537   ret = getop->init_processing();
3538   if (ret < 0) {
3539     ldout(s->cct, 20) << "serve_errordoc failed, init_processing ret=" << ret << dendl;
3540     return -1; // Trigger double error handler
3541   }
3542
3543   ret = getop->verify_op_mask();
3544   if (ret < 0) {
3545     ldout(s->cct, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret << dendl;
3546     return -1; // Trigger double error handler
3547   }
3548
3549   ret = getop->verify_permission();
3550   if (ret < 0) {
3551     ldout(s->cct, 20) << "serve_errordoc failed, verify_permission ret=" << ret << dendl;
3552     return -1; // Trigger double error handler
3553   }
3554
3555   ret = getop->verify_params();
3556   if (ret < 0) {
3557     ldout(s->cct, 20) << "serve_errordoc failed, verify_params ret=" << ret << dendl;
3558     return -1; // Trigger double error handler
3559   }
3560
3561   // No going back now
3562   getop->pre_exec();
3563   /*
3564    * FIXME Missing headers:
3565    * With a working errordoc, the s3 error fields are rendered as HTTP headers,
3566    *   x-amz-error-code: NoSuchKey
3567    *   x-amz-error-message: The specified key does not exist.
3568    *   x-amz-error-detail-Key: foo
3569    */
3570   getop->execute();
3571   getop->complete();
3572   return 0;
3573
3574 }
3575
3576 int RGWHandler_REST_S3Website::error_handler(int err_no,
3577                                             string* error_content) {
3578   int new_err_no = -1;
3579   rgw_http_errors::const_iterator r = rgw_http_s3_errors.find(err_no > 0 ? err_no : -err_no);
3580   int http_error_code = -1;
3581
3582   if (r != rgw_http_s3_errors.end()) {
3583     http_error_code = r->second.first;
3584   }
3585   ldout(s->cct, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no << " http_ret=" << http_error_code << dendl;
3586
3587   RGWBWRoutingRule rrule;
3588   bool should_redirect =
3589     s->bucket_info.website_conf.should_redirect(s->object.name, http_error_code,
3590                                                 &rrule);
3591
3592   if (should_redirect) {
3593     const string& hostname = s->info.env->get("HTTP_HOST", "");
3594     const string& protocol =
3595       (s->info.env->get("SERVER_PORT_SECURE") ? "https" : "http");
3596     int redirect_code = 0;
3597     rrule.apply_rule(protocol, hostname, s->object.name, &s->redirect,
3598                     &redirect_code);
3599     // Apply a custom HTTP response code
3600     if (redirect_code > 0)
3601       s->err.http_ret = redirect_code; // Apply a custom HTTP response code
3602     ldout(s->cct, 10) << "error handler redirect code=" << redirect_code
3603                       << " proto+host:" << protocol << "://" << hostname
3604                       << " -> " << s->redirect << dendl;
3605     return -ERR_WEBSITE_REDIRECT;
3606   } else if (err_no == -ERR_WEBSITE_REDIRECT) {
3607     // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
3608     // Do NOT fire the ErrorDoc handler
3609   } else if (!s->bucket_info.website_conf.error_doc.empty()) {
3610     /* This serves an entire page!
3611        On success, it will return zero, and no further content should be sent to the socket
3612        On failure, we need the double-error handler
3613      */
3614     new_err_no = RGWHandler_REST_S3Website::serve_errordoc(http_error_code, s->bucket_info.website_conf.error_doc);
3615     if (new_err_no && new_err_no != -1) {
3616       err_no = new_err_no;
3617     }
3618   } else {
3619     ldout(s->cct, 20) << "No special error handling today!" << dendl;
3620   }
3621
3622   return err_no;
3623 }
3624
3625 RGWOp* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data)
3626 {
3627   /** If we are in website mode, then it is explicitly impossible to run GET or
3628    * HEAD on the actual directory. We must convert the request to run on the
3629    * suffix object instead!
3630    */
3631   RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
3632   op->set_get_data(get_data);
3633   return op;
3634 }
3635
3636 RGWOp* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data)
3637 {
3638   /** If we are in website mode, then it is explicitly impossible to run GET or
3639    * HEAD on the actual directory. We must convert the request to run on the
3640    * suffix object instead!
3641    */
3642   RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
3643   op->set_get_data(get_data);
3644   return op;
3645 }
3646
3647 RGWOp* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data)
3648 {
3649   /** If we are in website mode, then it is explicitly impossible to run GET or
3650    * HEAD on the actual directory. We must convert the request to run on the
3651    * suffix object instead!
3652    */
3653   RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
3654   op->set_get_data(get_data);
3655   return op;
3656 }
3657
3658
3659 namespace rgw {
3660 namespace auth {
3661 namespace s3 {
3662
3663 bool AWSGeneralAbstractor::is_time_skew_ok(const utime_t& header_time,
3664                                            const bool qsr) const
3665 {
3666   /* Check for time skew first. */
3667   const time_t req_sec = header_time.sec();
3668   time_t now;
3669   time(&now);
3670
3671   if ((req_sec < now - RGW_AUTH_GRACE_MINS * 60 ||
3672        req_sec > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) {
3673     ldout(cct, 10) << "req_sec=" << req_sec << " now=" << now
3674                    << "; now - RGW_AUTH_GRACE_MINS="
3675                    << now - RGW_AUTH_GRACE_MINS * 60
3676                    << "; now + RGW_AUTH_GRACE_MINS="
3677                    << now + RGW_AUTH_GRACE_MINS * 60
3678                    << dendl;
3679
3680     ldout(cct, 0)  << "NOTICE: request time skew too big now="
3681                    << utime_t(now, 0)
3682                    << " req_time=" << header_time
3683                    << dendl;
3684     return false;
3685   } else {
3686     return true;
3687   }
3688 }
3689
3690
3691 static rgw::auth::Completer::cmplptr_t
3692 null_completer_factory(const boost::optional<std::string>& secret_key)
3693 {
3694   return nullptr;
3695 }
3696
3697
3698 AWSEngine::VersionAbstractor::auth_data_t
3699 AWSGeneralAbstractor::get_auth_data(const req_state* const s) const
3700 {
3701   AwsVersion version;
3702   AwsRoute route;
3703   std::tie(version, route) = discover_aws_flavour(s->info);
3704
3705   if (version == AwsVersion::V2) {
3706     return get_auth_data_v2(s);
3707   } else if (version == AwsVersion::V4) {
3708     return get_auth_data_v4(s, route == AwsRoute::QUERY_STRING);
3709   } else {
3710     /* FIXME(rzarzynski): handle anon user. */
3711     throw -EINVAL;
3712   }
3713 }
3714
3715 boost::optional<std::string>
3716 AWSGeneralAbstractor::get_v4_canonical_headers(
3717   const req_info& info,
3718   const boost::string_view& signedheaders,
3719   const bool using_qs) const
3720 {
3721   return rgw::auth::s3::get_v4_canonical_headers(info, signedheaders,
3722                                                  using_qs, false);
3723 }
3724
3725 AWSEngine::VersionAbstractor::auth_data_t
3726 AWSGeneralAbstractor::get_auth_data_v4(const req_state* const s,
3727                                        /* FIXME: const. */
3728                                        bool using_qs) const
3729 {
3730   boost::string_view access_key_id;
3731   boost::string_view signed_hdrs;
3732
3733   boost::string_view date;
3734   boost::string_view credential_scope;
3735   boost::string_view client_signature;
3736
3737   int ret = rgw::auth::s3::parse_credentials(s->info,
3738                                              access_key_id,
3739                                              credential_scope,
3740                                              signed_hdrs,
3741                                              client_signature,
3742                                              date,
3743                                              using_qs);
3744   if (ret < 0) {
3745     throw ret;
3746   }
3747
3748   /* craft canonical headers */
3749   boost::optional<std::string> canonical_headers = \
3750     get_v4_canonical_headers(s->info, signed_hdrs, using_qs);
3751   if (canonical_headers) {
3752     ldout(s->cct, 10) << "canonical headers format = " << *canonical_headers
3753                       << dendl;
3754   } else {
3755     throw -EPERM;
3756   }
3757
3758   /* Get the expected hash. */
3759   auto exp_payload_hash = rgw::auth::s3::get_v4_exp_payload_hash(s->info);
3760
3761   /* Craft canonical URI. Using std::move later so let it be non-const. */
3762   auto canonical_uri = rgw::auth::s3::get_v4_canonical_uri(s->info);
3763
3764   /* Craft canonical query string. std::moving later so non-const here. */
3765   auto canonical_qs = rgw::auth::s3::get_v4_canonical_qs(s->info, using_qs);
3766
3767   /* Craft canonical request. */
3768   auto canonical_req_hash = \
3769     rgw::auth::s3::get_v4_canon_req_hash(s->cct,
3770                                          s->info.method,
3771                                          std::move(canonical_uri),
3772                                          std::move(canonical_qs),
3773                                          std::move(*canonical_headers),
3774                                          signed_hdrs,
3775                                          exp_payload_hash);
3776
3777   auto string_to_sign = \
3778     rgw::auth::s3::get_v4_string_to_sign(s->cct,
3779                                          AWS4_HMAC_SHA256_STR,
3780                                          date,
3781                                          credential_scope,
3782                                          std::move(canonical_req_hash));
3783
3784   const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
3785                                      credential_scope,
3786                                      std::placeholders::_1,
3787                                      std::placeholders::_2,
3788                                      std::placeholders::_3);
3789
3790   /* Requests authenticated with the Query Parameters are treated as unsigned.
3791    * From "Authenticating Requests: Using Query Parameters (AWS Signature
3792    * Version 4)":
3793    *
3794    *   You don't include a payload hash in the Canonical Request, because
3795    *   when you create a presigned URL, you don't know the payload content
3796    *   because the URL is used to upload an arbitrary payload. Instead, you
3797    *   use a constant string UNSIGNED-PAYLOAD.
3798    *
3799    * This means we have absolutely no business in spawning completer. Both
3800    * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
3801    * by default. We don't need to change that. */
3802   if (is_v4_payload_unsigned(exp_payload_hash) || is_v4_payload_empty(s)) {
3803     return {
3804       access_key_id,
3805       client_signature,
3806       std::move(string_to_sign),
3807       sig_factory,
3808       null_completer_factory
3809     };
3810   } else {
3811     /* We're going to handle a signed payload. Be aware that even empty HTTP
3812      * body (no payload) requires verification:
3813      *
3814      *   The x-amz-content-sha256 header is required for all AWS Signature
3815      *   Version 4 requests. It provides a hash of the request payload. If
3816      *   there is no payload, you must provide the hash of an empty string. */
3817     if (!is_v4_payload_streamed(exp_payload_hash)) {
3818       ldout(s->cct, 10) << "delaying v4 auth" << dendl;
3819
3820       /* payload in a single chunk */
3821       switch (s->op_type)
3822       {
3823         case RGW_OP_CREATE_BUCKET:
3824         case RGW_OP_PUT_OBJ:
3825         case RGW_OP_PUT_ACLS:
3826         case RGW_OP_PUT_CORS:
3827         case RGW_OP_COMPLETE_MULTIPART:
3828         case RGW_OP_SET_BUCKET_VERSIONING:
3829         case RGW_OP_DELETE_MULTI_OBJ:
3830         case RGW_OP_ADMIN_SET_METADATA:
3831         case RGW_OP_SET_BUCKET_WEBSITE:
3832         case RGW_OP_PUT_BUCKET_POLICY:
3833         case RGW_OP_PUT_OBJ_TAGGING:
3834         case RGW_OP_PUT_LC:
3835           break;
3836         default:
3837           dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl;
3838           throw -ERR_NOT_IMPLEMENTED;
3839       }
3840
3841       const auto cmpl_factory = std::bind(AWSv4ComplSingle::create,
3842                                           s,
3843                                           std::placeholders::_1);
3844       return {
3845         access_key_id,
3846         client_signature,
3847         std::move(string_to_sign),
3848         sig_factory,
3849         cmpl_factory
3850       };
3851     } else {
3852       /* IMHO "streamed" doesn't fit too good here. I would prefer to call
3853        * it "chunked" but let's be coherent with Amazon's terminology. */
3854
3855       dout(10) << "body content detected in multiple chunks" << dendl;
3856
3857       /* payload in multiple chunks */
3858
3859       switch(s->op_type)
3860       {
3861         case RGW_OP_PUT_OBJ:
3862           break;
3863         default:
3864           dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl;
3865           throw -ERR_NOT_IMPLEMENTED;
3866       }
3867
3868       dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl;
3869
3870       /* In the case of streamed payload client sets the x-amz-content-sha256
3871        * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
3872        * when constructing the Canonical Request. */
3873
3874       /* In the case of single-chunk upload client set the header's value is
3875        * coherent with the one used for Canonical Request crafting. */
3876
3877       /* In the case of query string-based authentication there should be no
3878        * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
3879        * for CanonReq. */
3880       const auto cmpl_factory = std::bind(AWSv4ComplMulti::create,
3881                                           s,
3882                                           date,
3883                                           credential_scope,
3884                                           client_signature,
3885                                           std::placeholders::_1);
3886       return {
3887         access_key_id,
3888         client_signature,
3889         std::move(string_to_sign),
3890         sig_factory,
3891         cmpl_factory
3892       };
3893     }
3894   }
3895 }
3896
3897
3898 boost::optional<std::string>
3899 AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
3900   const req_info& info,
3901   const boost::string_view& signedheaders,
3902   const bool using_qs) const
3903 {
3904   return rgw::auth::s3::get_v4_canonical_headers(info, signedheaders,
3905                                                  using_qs, true);
3906 }
3907
3908
3909 AWSEngine::VersionAbstractor::auth_data_t
3910 AWSGeneralAbstractor::get_auth_data_v2(const req_state* const s) const
3911 {
3912   boost::string_view access_key_id;
3913   boost::string_view signature;
3914   bool qsr = false;
3915
3916   const char* http_auth = s->info.env->get("HTTP_AUTHORIZATION");
3917   if (! http_auth || http_auth[0] == '\0') {
3918     /* Credentials are provided in query string. We also need to verify
3919      * the "Expires" parameter now. */
3920     access_key_id = s->info.args.get("AWSAccessKeyId");
3921     signature = s->info.args.get("Signature");
3922     qsr = true;
3923
3924     boost::string_view expires = s->info.args.get("Expires");
3925     if (! expires.empty()) {
3926       /* It looks we have the guarantee that expires is a null-terminated,
3927        * and thus string_view::data() can be safely used. */
3928       const time_t exp = atoll(expires.data());
3929       time_t now;
3930       time(&now);
3931
3932       if (now >= exp) {
3933         throw -EPERM;
3934       }
3935     }
3936   } else {
3937     /* The "Authorization" HTTP header is being used. */
3938     const boost::string_view auth_str(http_auth + strlen("AWS "));
3939     const size_t pos = auth_str.rfind(':');
3940     if (pos != boost::string_view::npos) {
3941       access_key_id = auth_str.substr(0, pos);
3942       signature = auth_str.substr(pos + 1);
3943     }
3944   }
3945
3946   /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
3947   std::string string_to_sign;
3948   utime_t header_time;
3949   if (! rgw_create_s3_canonical_header(s->info, &header_time, string_to_sign,
3950         qsr)) {
3951     ldout(cct, 10) << "failed to create the canonized auth header\n"
3952                    << rgw::crypt_sanitize::auth{s,string_to_sign} << dendl;
3953     throw -EPERM;
3954   }
3955
3956   ldout(cct, 10) << "string_to_sign:\n"
3957                  << rgw::crypt_sanitize::auth{s,string_to_sign} << dendl;
3958
3959   if (! is_time_skew_ok(header_time, qsr)) {
3960     throw -ERR_REQUEST_TIME_SKEWED;
3961   }
3962
3963   return {
3964     std::move(access_key_id),
3965     std::move(signature),
3966     std::move(string_to_sign),
3967     rgw::auth::s3::get_v2_signature,
3968     null_completer_factory
3969   };
3970 }
3971
3972
3973 AWSEngine::VersionAbstractor::auth_data_t
3974 AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state* const s) const
3975 {
3976   return {
3977     s->auth.s3_postobj_creds.access_key,
3978     s->auth.s3_postobj_creds.signature,
3979     s->auth.s3_postobj_creds.encoded_policy.to_str(),
3980     rgw::auth::s3::get_v2_signature,
3981     null_completer_factory
3982   };
3983 }
3984
3985 AWSEngine::VersionAbstractor::auth_data_t
3986 AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state* const s) const
3987 {
3988   const boost::string_view credential = s->auth.s3_postobj_creds.x_amz_credential;
3989
3990   /* grab access key id */
3991   const size_t pos = credential.find("/");
3992   const boost::string_view access_key_id = credential.substr(0, pos);
3993   dout(10) << "access key id = " << access_key_id << dendl;
3994
3995   /* grab credential scope */
3996   const boost::string_view credential_scope = credential.substr(pos + 1);
3997   dout(10) << "credential scope = " << credential_scope << dendl;
3998
3999   const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
4000                                      credential_scope,
4001                                      std::placeholders::_1,
4002                                      std::placeholders::_2,
4003                                      std::placeholders::_3);
4004
4005   return {
4006     access_key_id,
4007     s->auth.s3_postobj_creds.signature,
4008     s->auth.s3_postobj_creds.encoded_policy.to_str(),
4009     sig_factory,
4010     null_completer_factory
4011   };
4012 }
4013
4014 AWSEngine::VersionAbstractor::auth_data_t
4015 AWSBrowserUploadAbstractor::get_auth_data(const req_state* const s) const
4016 {
4017   if (s->auth.s3_postobj_creds.x_amz_algorithm == AWS4_HMAC_SHA256_STR) {
4018     ldout(s->cct, 0) << "Signature verification algorithm AWS v4"
4019                      << " (AWS4-HMAC-SHA256)" << dendl;
4020     return get_auth_data_v4(s);
4021   } else {
4022     ldout(s->cct, 0) << "Signature verification algorithm AWS v2" << dendl;
4023     return get_auth_data_v2(s);
4024   }
4025 }
4026
4027
4028 AWSEngine::result_t
4029 AWSEngine::authenticate(const req_state* const s) const
4030 {
4031   /* Small reminder: an ver_abstractor is allowed to throw! */
4032   const auto auth_data = ver_abstractor.get_auth_data(s);
4033
4034   if (auth_data.access_key_id.empty() || auth_data.client_signature.empty()) {
4035     return result_t::deny(-EINVAL);
4036   } else {
4037     return authenticate(auth_data.access_key_id,
4038                         auth_data.client_signature,
4039                         auth_data.string_to_sign,
4040                         auth_data.signature_factory,
4041                         auth_data.completer_factory,
4042                         s);
4043   }
4044 }
4045
4046 } /* namespace s3 */
4047 } /* namespace auth */
4048 } /* namespace rgw */
4049
4050 rgw::LDAPHelper* rgw::auth::s3::LDAPEngine::ldh = nullptr;
4051 std::mutex rgw::auth::s3::LDAPEngine::mtx;
4052
4053 void rgw::auth::s3::LDAPEngine::init(CephContext* const cct)
4054 {
4055   if (! ldh) {
4056     std::lock_guard<std::mutex> lck(mtx);
4057     if (! ldh) {
4058       const string& ldap_uri = cct->_conf->rgw_ldap_uri;
4059       const string& ldap_binddn = cct->_conf->rgw_ldap_binddn;
4060       const string& ldap_searchdn = cct->_conf->rgw_ldap_searchdn;
4061       const string& ldap_searchfilter = cct->_conf->rgw_ldap_searchfilter;
4062       const string& ldap_dnattr = cct->_conf->rgw_ldap_dnattr;
4063       std::string ldap_bindpw = parse_rgw_ldap_bindpw(cct);
4064
4065       ldh = new rgw::LDAPHelper(ldap_uri, ldap_binddn, ldap_bindpw,
4066                                 ldap_searchdn, ldap_searchfilter, ldap_dnattr);
4067
4068       ldh->init();
4069       ldh->bind();
4070     }
4071   }
4072 }
4073
4074 rgw::auth::RemoteApplier::acl_strategy_t
4075 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
4076 {
4077   //This is based on the assumption that the default acl strategy in
4078   // get_perms_from_aclspec, will take care. Extra acl spec is not required.
4079   return nullptr;
4080 }
4081
4082 rgw::auth::RemoteApplier::AuthInfo
4083 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken& token) const noexcept
4084 {
4085   /* The short form of "using" can't be used here -- we're aliasing a class'
4086    * member. */
4087   using acct_privilege_t = \
4088     rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t;
4089
4090   return rgw::auth::RemoteApplier::AuthInfo {
4091     rgw_user(token.id),
4092     token.id,
4093     RGW_PERM_FULL_CONTROL,
4094     acct_privilege_t::IS_PLAIN_ACCT,
4095     TYPE_LDAP
4096   };
4097 }
4098
4099 rgw::auth::Engine::result_t
4100 rgw::auth::s3::LDAPEngine::authenticate(
4101   const boost::string_view& access_key_id,
4102   const boost::string_view& signature,
4103   const string_to_sign_t& string_to_sign,
4104   const signature_factory_t&,
4105   const completer_factory_t& completer_factory,
4106   const req_state* const s) const
4107 {
4108   /* boost filters and/or string_ref may throw on invalid input */
4109   rgw::RGWToken base64_token;
4110   try {
4111     base64_token = rgw::from_base64(access_key_id);
4112   } catch (...) {
4113     base64_token = std::string("");
4114   }
4115
4116   if (! base64_token.valid()) {
4117     return result_t::deny();
4118   }
4119
4120   //TODO: Uncomment, when we have a migration plan in place.
4121   //Check if a user of type other than 'ldap' is already present, if yes, then
4122   //return error.
4123   /*RGWUserInfo user_info;
4124   user_info.user_id = base64_token.id;
4125   if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
4126     if (user_info.type != TYPE_LDAP) {
4127       ldout(cct, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
4128       return nullptr;
4129     }
4130   }*/
4131
4132   if (ldh->auth(base64_token.id, base64_token.key) != 0) {
4133     return result_t::deny();
4134   }
4135
4136   auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(),
4137                                             get_creds_info(base64_token));
4138   return result_t::grant(std::move(apl), completer_factory(boost::none));
4139 }
4140
4141
4142 /* LocalEndgine */
4143 rgw::auth::Engine::result_t
4144 rgw::auth::s3::LocalEngine::authenticate(
4145   const boost::string_view& _access_key_id,
4146   const boost::string_view& signature,
4147   const string_to_sign_t& string_to_sign,
4148   const signature_factory_t& signature_factory,
4149   const completer_factory_t& completer_factory,
4150   const req_state* const s) const
4151 {
4152   /* get the user info */
4153   RGWUserInfo user_info;
4154   /* TODO(rzarzynski): we need to have string-view taking variant. */
4155   const std::string access_key_id = _access_key_id.to_string();
4156   if (rgw_get_user_info_by_access_key(store, access_key_id, user_info) < 0) {
4157       ldout(cct, 5) << "error reading user info, uid=" << access_key_id
4158               << " can't authenticate" << dendl;
4159       return result_t::deny(-ERR_INVALID_ACCESS_KEY);
4160   }
4161   //TODO: Uncomment, when we have a migration plan in place.
4162   /*else {
4163     if (s->user->type != TYPE_RGW) {
4164       ldout(cct, 10) << "ERROR: User id of type: " << s->user->type
4165                      << " is present" << dendl;
4166       throw -EPERM;
4167     }
4168   }*/
4169
4170   const auto iter = user_info.access_keys.find(access_key_id);
4171   if (iter == std::end(user_info.access_keys)) {
4172     ldout(cct, 0) << "ERROR: access key not encoded in user info" << dendl;
4173     return result_t::deny(-EPERM);
4174   }
4175   const RGWAccessKey& k = iter->second;
4176
4177   const VersionAbstractor::server_signature_t server_signature = \
4178     signature_factory(cct, k.key, string_to_sign);
4179
4180   ldout(cct, 15) << "string_to_sign="
4181                  << rgw::crypt_sanitize::log_content{string_to_sign}
4182                  << dendl;
4183   ldout(cct, 15) << "server signature=" << server_signature << dendl;
4184   ldout(cct, 15) << "client signature=" << signature << dendl;
4185   ldout(cct, 15) << "compare=" << signature.compare(server_signature) << dendl;
4186
4187   if (static_cast<boost::string_view>(server_signature) != signature) {
4188     return result_t::deny(-ERR_SIGNATURE_NO_MATCH);
4189   }
4190
4191   auto apl = apl_factory->create_apl_local(cct, s, user_info, k.subuser);
4192   return result_t::grant(std::move(apl), completer_factory(k.key));
4193 }
4194
4195 bool rgw::auth::s3::S3AnonymousEngine::is_applicable(
4196   const req_state* s
4197 ) const noexcept {
4198   if (s->op == OP_OPTIONS) {
4199     return true;
4200   }
4201
4202   AwsVersion version;
4203   AwsRoute route;
4204   std::tie(version, route) = discover_aws_flavour(s->info);
4205
4206   return route == AwsRoute::QUERY_STRING && version == AwsVersion::UNKOWN;
4207 }