PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/rgw/rgw_auth_s3.cc

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