diff --git a/src/rgw/rgw_keystone.cc b/src/rgw/rgw_keystone.cc index 5e9d63af28bf4..ae390feb8e740 100644 --- a/src/rgw/rgw_keystone.cc +++ b/src/rgw/rgw_keystone.cc @@ -11,9 +11,129 @@ #include "rgw_common.h" #include "rgw_keystone.h" +#include "common/ceph_crypto_cms.h" +#include "common/armor.h" #define dout_subsys ceph_subsys_rgw +int rgw_open_cms_envelope(CephContext * const cct, string& src, string& dst) +{ +#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; +} + + KeystoneApiVersion KeystoneService::get_api_version() { const int keystone_version = g_ceph_context->_conf->rgw_keystone_api_version; @@ -40,7 +160,9 @@ bool KeystoneToken::has_role(const string& r) return false; } -int KeystoneToken::parse(CephContext *cct, bufferlist& bl) +int KeystoneToken::parse(CephContext * const cct, + const string& token_str, + bufferlist& bl) { JSONParser parser; if (!parser.parse(bl.c_str(), bl.length())) { @@ -56,11 +178,17 @@ int KeystoneToken::parse(CephContext *cct, bufferlist& bl) /* Token structure doesn't follow Identity API v2, so the token * must be in v3. Otherwise we can assume it's wrongly formatted. */ JSONDecoder::decode_json("token", *this, &parser, true); + token.id = token_str; } } else if (version == KeystoneApiVersion::VER_3) { if (!JSONDecoder::decode_json("token", *this, &parser)) { /* If the token cannot be parsed according to V3, try V2. */ JSONDecoder::decode_json("access", *this, &parser, true); + } else { + /* 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 { return -ENOTSUP; @@ -103,7 +231,15 @@ bool RGWKeystoneTokenCache::find(const string& token_id, KeystoneToken& token) return true; } -void RGWKeystoneTokenCache::add(const string& token_id, KeystoneToken& token) +bool RGWKeystoneTokenCache::find_admin(KeystoneToken& token) +{ + Mutex::Locker l(lock); + + return find(admin_token_id, token); +} + +void RGWKeystoneTokenCache::add(const string& token_id, + const KeystoneToken& token) { lock.Lock(); map::iterator iter = tokens.find(token_id); @@ -128,6 +264,14 @@ void RGWKeystoneTokenCache::add(const string& token_id, KeystoneToken& token) lock.Unlock(); } +void RGWKeystoneTokenCache::add_admin(const KeystoneToken& token) +{ + Mutex::Locker l(lock); + + rgw_get_token_id(token.token.id, admin_token_id); + add(admin_token_id, token); +} + void RGWKeystoneTokenCache::invalidate(const string& token_id) { Mutex::Locker l(lock); diff --git a/src/rgw/rgw_keystone.h b/src/rgw/rgw_keystone.h index ba3f609ec22d9..ffbae9008b762 100644 --- a/src/rgw/rgw_keystone.h +++ b/src/rgw/rgw_keystone.h @@ -6,6 +6,16 @@ #include "rgw_common.h" +int rgw_open_cms_envelope(CephContext *cct, string& src, string& dst); +int rgw_decode_b64_cms(CephContext *cct, + const string& signed_b64, + bufferlist& bl); +bool rgw_is_pki_token(const string& token); +void rgw_get_token_id(const string& token, string& token_id); +bool rgw_decode_pki_token(CephContext *cct, + const string& token, + bufferlist& bl); + enum class KeystoneApiVersion { VER_2, VER_3 @@ -77,18 +87,21 @@ class KeystoneToken { uint64_t now = ceph_clock_now(NULL).sec(); return (now >= (uint64_t)get_expires()); } - int parse(CephContext *cct, bufferlist& bl); + int parse(CephContext *cct, + const string& token_str, + bufferlist& bl /* in */); void decode_json(JSONObj *access_obj); }; -struct token_entry { - KeystoneToken token; - list::iterator lru_iter; -}; - class RGWKeystoneTokenCache { + struct token_entry { + KeystoneToken token; + list::iterator lru_iter; + }; + CephContext *cct; + string admin_token_id; map tokens; list tokens_lru; @@ -97,10 +110,16 @@ class RGWKeystoneTokenCache { size_t max; public: - RGWKeystoneTokenCache(CephContext *_cct, int _max) : cct(_cct), lock("RGWKeystoneTokenCache"), max(_max) {} + RGWKeystoneTokenCache(CephContext *_cct, int _max) + : cct(_cct), + lock("RGWKeystoneTokenCache", true /* recursive */), + max(_max) { + } bool find(const string& token_id, KeystoneToken& token); - void add(const string& token_id, KeystoneToken& token); + bool find_admin(KeystoneToken& token); + void add(const string& token_id, const KeystoneToken& token); + void add_admin(const KeystoneToken& token); void invalidate(const string& token_id); }; diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 4adebb803fe36..829bd3598a533 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -2811,7 +2811,7 @@ int RGW_Auth_S3_Keystone_ValidateToken::validate_s3token( } /* now parse response */ - if (response.parse(cct, rx_buffer) < 0) { + if (response.parse(cct, string(), rx_buffer) < 0) { dout(2) << "s3 keystone: token parsing failed" << dendl; return -EPERM; } diff --git a/src/rgw/rgw_swift.cc b/src/rgw/rgw_swift.cc index 3d2e707087785..4e8c00989651d 100644 --- a/src/rgw/rgw_swift.cc +++ b/src/rgw/rgw_swift.cc @@ -15,9 +15,6 @@ #include "include/str_list.h" -#include "common/ceph_crypto_cms.h" -#include "common/armor.h" - #define dout_subsys ceph_subsys_rgw static list roles_list; @@ -184,74 +181,6 @@ typedef RGWPostHTTPData RGWGetRevokedTokens; static RGWKeystoneTokenCache *keystone_token_cache = NULL; -static int open_cms_envelope(CephContext *cct, string& src, string& dst) -{ -#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; -} - -static int decode_b64_cms(CephContext *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; -} - int RGWSwift::get_keystone_url(CephContext * const cct, std::string& url) { @@ -283,12 +212,24 @@ int RGWSwift::get_keystone_admin_token(CephContext * const cct, { std::string token_url; - if (get_keystone_url(cct, token_url) < 0) + if (get_keystone_url(cct, token_url) < 0) { return -EINVAL; + } + if (!cct->_conf->rgw_keystone_admin_token.empty()) { token = cct->_conf->rgw_keystone_admin_token; return 0; } + + KeystoneToken t; + + /* Try cache first. */ + if (keystone_token_cache->find_admin(t)) { + ldout(cct, 20) << "found cached admin token" << dendl; + token = t.token.id; + return 0; + } + bufferlist token_bl; RGWGetKeystoneAdminToken token_req(cct, &token_bl, cct->_conf->rgw_keystone_verify_ssl); token_req.append_header("Content-Type", "application/json"); @@ -304,14 +245,7 @@ int RGWSwift::get_keystone_admin_token(CephContext * const cct, token_req.set_post_data(ss.str()); token_req.set_send_length(ss.str().length()); token_url.append("v2.0/tokens"); - int ret = token_req.process("POST", token_url.c_str()); - if (ret < 0) - return ret; - KeystoneToken t; - if (t.parse(cct, token_bl) != 0) - return -EINVAL; - token = t.token.id; - return 0; + } else if (keystone_version == KeystoneApiVersion::VER_3) { KeystoneAdminTokenRequestVer3 req_serializer(cct); req_serializer.dump(&jf); @@ -321,13 +255,21 @@ int RGWSwift::get_keystone_admin_token(CephContext * const cct, token_req.set_post_data(ss.str()); token_req.set_send_length(ss.str().length()); token_url.append("v3/auth/tokens"); - int ret = token_req.process("POST", token_url.c_str()); - if (ret < 0) - return ret; - token = token_req.get_subject_token(); - return 0; + } else { + return -ENOTSUP; } - return -EINVAL; + + const int ret = token_req.process("POST", token_url.c_str()); + if (ret < 0) { + return ret; + } + if (t.parse(cct, token_req.get_subject_token(), token_bl) != 0) { + return -EINVAL; + } + + keystone_token_cache->add_admin(t); + token = t.token.id; + return 0; } @@ -384,14 +326,14 @@ int RGWSwift::check_revoked() ldout(cct, 10) << "signed=" << signed_str << dendl; string signed_b64; - ret = open_cms_envelope(cct, signed_str, 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 = decode_b64_cms(cct, signed_b64, json); + ret = rgw_decode_b64_cms(cct, signed_b64, json); if (ret < 0) { return ret; } @@ -436,11 +378,15 @@ static void rgw_set_keystone_token_auth_info(KeystoneToken& token, struct rgw_sw info->status = 200; } -int RGWSwift::parse_keystone_token_response(const string& token, bufferlist& bl, struct rgw_swift_auth_info *info, KeystoneToken& t) +int RGWSwift::parse_keystone_token_response(const string& token, + bufferlist& bl, + struct rgw_swift_auth_info *info, + KeystoneToken& t) { - int ret = t.parse(cct, bl); - if (ret < 0) + int ret = t.parse(cct, token, bl); + if (ret < 0) { return ret; + } bool found = false; list::iterator iter; @@ -481,53 +427,13 @@ int RGWSwift::update_user_info(RGWRados *store, struct rgw_swift_auth_info *info return 0; } -#define PKI_ANS1_PREFIX "MII" - -static bool is_pki_token(const string& token) -{ - return token.compare(0, sizeof(PKI_ANS1_PREFIX) - 1, PKI_ANS1_PREFIX) == 0; -} - -static void get_token_id(const string& token, string& token_id) -{ - if (!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; -} - -static bool decode_pki_token(CephContext *cct, const string& token, bufferlist& bl) -{ - if (!is_pki_token(token)) - return false; - - int ret = decode_b64_cms(cct, token, bl); - if (ret < 0) - return false; - - ldout(cct, 20) << "successfully decoded pki token" << dendl; - - return true; -} - int RGWSwift::validate_keystone_token(RGWRados *store, const string& token, struct rgw_swift_auth_info *info, RGWUserInfo& rgw_user) { KeystoneToken t; string token_id; - get_token_id(token, token_id); + rgw_get_token_id(token, token_id); ldout(cct, 20) << "token_id=" << token_id << dendl; @@ -547,7 +453,7 @@ int RGWSwift::validate_keystone_token(RGWRados *store, const string& token, stru bufferlist bl; /* check if that's a self signed token that we can decode */ - if (!decode_pki_token(cct, token, bl)) { + if (!rgw_decode_pki_token(cct, token, bl)) { /* can't decode, just go to the keystone server for validation */ diff --git a/src/rgw/rgw_swift.h b/src/rgw/rgw_swift.h index c87adee075c6c..3bffc26dae1a3 100644 --- a/src/rgw/rgw_swift.h +++ b/src/rgw/rgw_swift.h @@ -31,7 +31,9 @@ class RGWSwift { int validate_keystone_token(RGWRados *store, const string& token, struct rgw_swift_auth_info *info, RGWUserInfo& rgw_user); - int parse_keystone_token_response(const string& token, bufferlist& bl, struct rgw_swift_auth_info *info, + int parse_keystone_token_response(const string& token, + bufferlist& bl, + struct rgw_swift_auth_info *info, KeystoneToken& t); int update_user_info(RGWRados *store, struct rgw_swift_auth_info *info, RGWUserInfo& user_info); int get_keystone_url(std::string& url);