X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Frgw%2Frgw_keystone.cc;fp=src%2Fceph%2Fsrc%2Frgw%2Frgw_keystone.cc;h=0000000000000000000000000000000000000000;hb=7da45d65be36d36b880cc55c5036e96c24b53f00;hp=3294380ba100c5b580d6689f6dda8ea078098383;hpb=691462d09d0987b47e112d6ee8740375df3c51b2;p=stor4nfv.git diff --git a/src/ceph/src/rgw/rgw_keystone.cc b/src/ceph/src/rgw/rgw_keystone.cc deleted file mode 100644 index 3294380..0000000 --- a/src/ceph/src/rgw/rgw_keystone.cc +++ /dev/null @@ -1,649 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#include -#include - -#include - -#include "common/errno.h" -#include "common/ceph_json.h" -#include "include/types.h" -#include "include/str_list.h" - -#include "rgw_common.h" -#include "rgw_keystone.h" -#include "common/ceph_crypto_cms.h" -#include "common/armor.h" -#include "common/Cond.h" - -#define dout_context g_ceph_context -#define dout_subsys ceph_subsys_rgw - -int rgw_open_cms_envelope(CephContext * const cct, - const std::string& src, - std::string& dst) /* out */ -{ -#define BEGIN_CMS "-----BEGIN CMS-----" -#define END_CMS "-----END CMS-----" - - int start = src.find(BEGIN_CMS); - if (start < 0) { - ldout(cct, 0) << "failed to find " << BEGIN_CMS << " in response" << dendl; - return -EINVAL; - } - start += sizeof(BEGIN_CMS) - 1; - - int end = src.find(END_CMS); - if (end < 0) { - ldout(cct, 0) << "failed to find " << END_CMS << " in response" << dendl; - return -EINVAL; - } - - string s = src.substr(start, end - start); - - int pos = 0; - - do { - int next = s.find('\n', pos); - if (next < 0) { - dst.append(s.substr(pos)); - break; - } else { - dst.append(s.substr(pos, next - pos)); - } - pos = next + 1; - } while (pos < (int)s.size()); - - return 0; -} - -int rgw_decode_b64_cms(CephContext * const cct, - const string& signed_b64, - bufferlist& bl) -{ - bufferptr signed_ber(signed_b64.size() * 2); - char *dest = signed_ber.c_str(); - const char *src = signed_b64.c_str(); - size_t len = signed_b64.size(); - char buf[len + 1]; - buf[len] = '\0'; - - for (size_t i = 0; i < len; i++, src++) { - if (*src != '-') { - buf[i] = *src; - } else { - buf[i] = '/'; - } - } - - int ret = ceph_unarmor(dest, dest + signed_ber.length(), buf, - buf + signed_b64.size()); - if (ret < 0) { - ldout(cct, 0) << "ceph_unarmor() failed, ret=" << ret << dendl; - return ret; - } - - bufferlist signed_ber_bl; - signed_ber_bl.append(signed_ber); - - ret = ceph_decode_cms(cct, signed_ber_bl, bl); - if (ret < 0) { - ldout(cct, 0) << "ceph_decode_cms returned " << ret << dendl; - return ret; - } - - return 0; -} - -#define PKI_ANS1_PREFIX "MII" - -bool rgw_is_pki_token(const string& token) -{ - return token.compare(0, sizeof(PKI_ANS1_PREFIX) - 1, PKI_ANS1_PREFIX) == 0; -} - -void rgw_get_token_id(const string& token, string& token_id) -{ - if (!rgw_is_pki_token(token)) { - token_id = token; - return; - } - - unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE]; - - MD5 hash; - hash.Update((const byte *)token.c_str(), token.size()); - hash.Final(m); - - char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1]; - buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5); - token_id = calc_md5; -} - -bool rgw_decode_pki_token(CephContext * const cct, - const string& token, - bufferlist& bl) -{ - if (!rgw_is_pki_token(token)) { - return false; - } - - int ret = rgw_decode_b64_cms(cct, token, bl); - if (ret < 0) { - return false; - } - - ldout(cct, 20) << "successfully decoded pki token" << dendl; - - return true; -} - - -namespace rgw { -namespace keystone { - -ApiVersion CephCtxConfig::get_api_version() const noexcept -{ - switch (g_ceph_context->_conf->rgw_keystone_api_version) { - case 3: - return ApiVersion::VER_3; - case 2: - return ApiVersion::VER_2; - default: - dout(0) << "ERROR: wrong Keystone API version: " - << g_ceph_context->_conf->rgw_keystone_api_version - << "; falling back to v2" << dendl; - return ApiVersion::VER_2; - } -} - -std::string CephCtxConfig::get_endpoint_url() const noexcept -{ - static const std::string url = g_ceph_context->_conf->rgw_keystone_url; - - if (url.empty() || boost::algorithm::ends_with(url, "/")) { - return url; - } else { - static const std::string url_normalised = url + '/'; - return url_normalised; - } -} - -int Service::get_admin_token(CephContext* const cct, - TokenCache& token_cache, - const Config& config, - std::string& token) -{ - /* Let's check whether someone uses the deprecated "admin token" feauture - * based on a shared secret from keystone.conf file. */ - const auto& admin_token = config.get_admin_token(); - if (! admin_token.empty()) { - token = std::string(admin_token.data(), admin_token.length()); - return 0; - } - - TokenEnvelope t; - - /* Try cache first before calling Keystone for a new admin token. */ - if (token_cache.find_admin(t)) { - ldout(cct, 20) << "found cached admin token" << dendl; - token = t.token.id; - return 0; - } - - /* Call Keystone now. */ - const auto ret = issue_admin_token_request(cct, config, t); - if (! ret) { - token_cache.add_admin(t); - token = t.token.id; - } - - return ret; -} - -int Service::issue_admin_token_request(CephContext* const cct, - const Config& config, - TokenEnvelope& t) -{ - std::string token_url = config.get_endpoint_url(); - if (token_url.empty()) { - return -EINVAL; - } - - bufferlist token_bl; - RGWGetKeystoneAdminToken token_req(cct, &token_bl); - token_req.append_header("Content-Type", "application/json"); - JSONFormatter jf; - - const auto keystone_version = config.get_api_version(); - if (keystone_version == ApiVersion::VER_2) { - AdminTokenRequestVer2 req_serializer(config); - req_serializer.dump(&jf); - - std::stringstream ss; - jf.flush(ss); - token_req.set_post_data(ss.str()); - token_req.set_send_length(ss.str().length()); - token_url.append("v2.0/tokens"); - - } else if (keystone_version == ApiVersion::VER_3) { - AdminTokenRequestVer3 req_serializer(config); - req_serializer.dump(&jf); - - std::stringstream ss; - jf.flush(ss); - token_req.set_post_data(ss.str()); - token_req.set_send_length(ss.str().length()); - token_url.append("v3/auth/tokens"); - } else { - return -ENOTSUP; - } - - const int ret = token_req.process("POST", token_url.c_str()); - if (ret < 0) { - return ret; - } - - /* Detect rejection earlier than during the token parsing step. */ - if (token_req.get_http_status() == - RGWGetKeystoneAdminToken::HTTP_STATUS_UNAUTHORIZED) { - return -EACCES; - } - - if (t.parse(cct, token_req.get_subject_token(), token_bl, - keystone_version) != 0) { - return -EINVAL; - } - - return 0; -} - -int Service::get_keystone_barbican_token(CephContext * const cct, - std::string& token) -{ - using keystone_config_t = rgw::keystone::CephCtxConfig; - using keystone_cache_t = rgw::keystone::TokenCache; - - auto& config = keystone_config_t::get_instance(); - auto& token_cache = keystone_cache_t::get_instance(); - - std::string token_url = config.get_endpoint_url(); - if (token_url.empty()) { - return -EINVAL; - } - - rgw::keystone::TokenEnvelope t; - - /* Try cache first. */ - if (token_cache.find_barbican(t)) { - ldout(cct, 20) << "found cached barbican token" << dendl; - token = t.token.id; - return 0; - } - - bufferlist token_bl; - RGWKeystoneHTTPTransceiver token_req(cct, &token_bl); - token_req.append_header("Content-Type", "application/json"); - JSONFormatter jf; - - const auto keystone_version = config.get_api_version(); - if (keystone_version == ApiVersion::VER_2) { - rgw::keystone::BarbicanTokenRequestVer2 req_serializer(cct); - req_serializer.dump(&jf); - - std::stringstream ss; - jf.flush(ss); - token_req.set_post_data(ss.str()); - token_req.set_send_length(ss.str().length()); - token_url.append("v2.0/tokens"); - - } else if (keystone_version == ApiVersion::VER_3) { - BarbicanTokenRequestVer3 req_serializer(cct); - req_serializer.dump(&jf); - - std::stringstream ss; - jf.flush(ss); - token_req.set_post_data(ss.str()); - token_req.set_send_length(ss.str().length()); - token_url.append("v3/auth/tokens"); - } else { - return -ENOTSUP; - } - - ldout(cct, 20) << "Requesting secret from barbican url=" << token_url << dendl; - const int ret = token_req.process("POST", token_url.c_str()); - if (ret < 0) { - ldout(cct, 20) << "Barbican process error:" << token_bl.c_str() << dendl; - return ret; - } - - /* Detect rejection earlier than during the token parsing step. */ - if (token_req.get_http_status() == - RGWKeystoneHTTPTransceiver::HTTP_STATUS_UNAUTHORIZED) { - return -EACCES; - } - - if (t.parse(cct, token_req.get_subject_token(), token_bl, - keystone_version) != 0) { - return -EINVAL; - } - - token_cache.add_barbican(t); - token = t.token.id; - return 0; -} - - -bool TokenEnvelope::has_role(const std::string& r) const -{ - list::const_iterator iter; - for (iter = roles.cbegin(); iter != roles.cend(); ++iter) { - if (fnmatch(r.c_str(), ((*iter).name.c_str()), 0) == 0) { - return true; - } - } - return false; -} - -int TokenEnvelope::parse(CephContext* const cct, - const std::string& token_str, - ceph::bufferlist& bl, - const ApiVersion version) -{ - JSONParser parser; - if (! parser.parse(bl.c_str(), bl.length())) { - ldout(cct, 0) << "Keystone token parse error: malformed json" << dendl; - return -EINVAL; - } - - JSONObjIter token_iter = parser.find_first("token"); - JSONObjIter access_iter = parser.find_first("access"); - - try { - if (version == rgw::keystone::ApiVersion::VER_2) { - if (! access_iter.end()) { - decode_v2(*access_iter); - } else if (! token_iter.end()) { - /* TokenEnvelope structure doesn't follow Identity API v2, so let's - * fallback to v3. Otherwise we can assume it's wrongly formatted. - * The whole mechanism is a workaround for s3_token middleware that - * speaks in v2 disregarding the promise to go with v3. */ - decode_v3(*token_iter); - - /* Identity v3 conveys the token inforamtion not as a part of JSON but - * in the X-Subject-Token HTTP header we're getting from caller. */ - token.id = token_str; - } else { - return -EINVAL; - } - } else if (version == rgw::keystone::ApiVersion::VER_3) { - if (! token_iter.end()) { - decode_v3(*token_iter); - /* v3 suceeded. We have to fill token.id from external input as it - * isn't a part of the JSON response anymore. It has been moved - * to X-Subject-Token HTTP header instead. */ - token.id = token_str; - } else if (! access_iter.end()) { - /* If the token cannot be parsed according to V3, try V2. */ - decode_v2(*access_iter); - } else { - return -EINVAL; - } - } else { - return -ENOTSUP; - } - } catch (JSONDecoder::err& err) { - ldout(cct, 0) << "Keystone token parse error: " << err.message << dendl; - return -EINVAL; - } - - return 0; -} - -bool TokenCache::find(const std::string& token_id, - rgw::keystone::TokenEnvelope& token) -{ - Mutex::Locker l(lock); - return find_locked(token_id, token); -} - -bool TokenCache::find_locked(const std::string& token_id, - rgw::keystone::TokenEnvelope& token) -{ - assert(lock.is_locked_by_me()); - map::iterator iter = tokens.find(token_id); - if (iter == tokens.end()) { - if (perfcounter) perfcounter->inc(l_rgw_keystone_token_cache_miss); - return false; - } - - token_entry& entry = iter->second; - tokens_lru.erase(entry.lru_iter); - - if (entry.token.expired()) { - tokens.erase(iter); - if (perfcounter) perfcounter->inc(l_rgw_keystone_token_cache_hit); - return false; - } - token = entry.token; - - tokens_lru.push_front(token_id); - entry.lru_iter = tokens_lru.begin(); - - if (perfcounter) perfcounter->inc(l_rgw_keystone_token_cache_hit); - - return true; -} - -bool TokenCache::find_admin(rgw::keystone::TokenEnvelope& token) -{ - Mutex::Locker l(lock); - - return find_locked(admin_token_id, token); -} - -bool TokenCache::find_barbican(rgw::keystone::TokenEnvelope& token) -{ - Mutex::Locker l(lock); - - return find_locked(barbican_token_id, token); -} - -void TokenCache::add(const std::string& token_id, - const rgw::keystone::TokenEnvelope& token) -{ - Mutex::Locker l(lock); - add_locked(token_id, token); -} - -void TokenCache::add_locked(const std::string& token_id, - const rgw::keystone::TokenEnvelope& token) -{ - assert(lock.is_locked_by_me()); - map::iterator iter = tokens.find(token_id); - if (iter != tokens.end()) { - token_entry& e = iter->second; - tokens_lru.erase(e.lru_iter); - } - - tokens_lru.push_front(token_id); - token_entry& entry = tokens[token_id]; - entry.token = token; - entry.lru_iter = tokens_lru.begin(); - - while (tokens_lru.size() > max) { - list::reverse_iterator riter = tokens_lru.rbegin(); - iter = tokens.find(*riter); - assert(iter != tokens.end()); - tokens.erase(iter); - tokens_lru.pop_back(); - } -} - -void TokenCache::add_admin(const rgw::keystone::TokenEnvelope& token) -{ - Mutex::Locker l(lock); - - rgw_get_token_id(token.token.id, admin_token_id); - add_locked(admin_token_id, token); -} - -void TokenCache::add_barbican(const rgw::keystone::TokenEnvelope& token) -{ - Mutex::Locker l(lock); - - rgw_get_token_id(token.token.id, barbican_token_id); - add_locked(barbican_token_id, token); -} - -void TokenCache::invalidate(const std::string& token_id) -{ - Mutex::Locker l(lock); - map::iterator iter = tokens.find(token_id); - if (iter == tokens.end()) - return; - - ldout(cct, 20) << "invalidating revoked token id=" << token_id << dendl; - token_entry& e = iter->second; - tokens_lru.erase(e.lru_iter); - tokens.erase(iter); -} - -int TokenCache::RevokeThread::check_revoked() -{ - std::string url; - std::string token; - - bufferlist bl; - RGWGetRevokedTokens req(cct, &bl); - - if (rgw::keystone::Service::get_admin_token(cct, *cache, config, token) < 0) { - return -EINVAL; - } - - url = config.get_endpoint_url(); - if (url.empty()) { - return -EINVAL; - } - - req.append_header("X-Auth-Token", token); - - const auto keystone_version = config.get_api_version(); - if (keystone_version == rgw::keystone::ApiVersion::VER_2) { - url.append("v2.0/tokens/revoked"); - } else if (keystone_version == rgw::keystone::ApiVersion::VER_3) { - url.append("v3/auth/tokens/OS-PKI/revoked"); - } - - req.set_send_length(0); - int ret = req.process(url.c_str()); - if (ret < 0) { - return ret; - } - - bl.append((char)0); // NULL terminate for debug output - - ldout(cct, 10) << "request returned " << bl.c_str() << dendl; - - JSONParser parser; - - if (!parser.parse(bl.c_str(), bl.length())) { - ldout(cct, 0) << "malformed json" << dendl; - return -EINVAL; - } - - JSONObjIter iter = parser.find_first("signed"); - if (iter.end()) { - ldout(cct, 0) << "revoked tokens response is missing signed section" << dendl; - return -EINVAL; - } - - JSONObj *signed_obj = *iter; - const std::string signed_str = signed_obj->get_data(); - - ldout(cct, 10) << "signed=" << signed_str << dendl; - - std::string signed_b64; - ret = rgw_open_cms_envelope(cct, signed_str, signed_b64); - if (ret < 0) { - return ret; - } - - ldout(cct, 10) << "content=" << signed_b64 << dendl; - - bufferlist json; - ret = rgw_decode_b64_cms(cct, signed_b64, json); - if (ret < 0) { - return ret; - } - - ldout(cct, 10) << "ceph_decode_cms: decoded: " << json.c_str() << dendl; - - JSONParser list_parser; - if (!list_parser.parse(json.c_str(), json.length())) { - ldout(cct, 0) << "malformed json" << dendl; - return -EINVAL; - } - - JSONObjIter revoked_iter = list_parser.find_first("revoked"); - if (revoked_iter.end()) { - ldout(cct, 0) << "no revoked section in json" << dendl; - return -EINVAL; - } - - JSONObj *revoked_obj = *revoked_iter; - - JSONObjIter tokens_iter = revoked_obj->find_first(); - for (; !tokens_iter.end(); ++tokens_iter) { - JSONObj *o = *tokens_iter; - - JSONObj *token = o->find_obj("id"); - if (!token) { - ldout(cct, 0) << "bad token in array, missing id" << dendl; - continue; - } - - const std::string token_id = token->get_data(); - cache->invalidate(token_id); - } - - return 0; -} - -bool TokenCache::going_down() const -{ - return down_flag; -} - -void* TokenCache::RevokeThread::entry() -{ - do { - ldout(cct, 2) << "keystone revoke thread: start" << dendl; - int r = check_revoked(); - if (r < 0) { - ldout(cct, 0) << "ERROR: keystone revocation processing returned error r=" - << r << dendl; - } - - if (cache->going_down()) { - break; - } - - lock.Lock(); - cond.WaitInterval(lock, - utime_t(cct->_conf->rgw_keystone_revocation_interval, 0)); - lock.Unlock(); - } while (!cache->going_down()); - - return nullptr; -} - -void TokenCache::RevokeThread::stop() -{ - Mutex::Locker l(lock); - cond.Signal(); -} - -}; /* namespace keystone */ -}; /* namespace rgw */