Skip to content

Commit

Permalink
Merge pull request #7630 from rzarzynski/wip-rgw-admin-token-cache
Browse files Browse the repository at this point in the history
rgw: add support for caching of Keystone admin token.

Reviewed-by: Yehuda Sadeh <yehuda@redhat.com>
  • Loading branch information
yehudasa committed Feb 27, 2016
2 parents 04884be + 155f079 commit 894738f
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 145 deletions.
148 changes: 146 additions & 2 deletions src/rgw/rgw_keystone.cc
Expand Up @@ -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;
Expand All @@ -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())) {
Expand All @@ -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;
Expand Down Expand Up @@ -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<string, token_entry>::iterator iter = tokens.find(token_id);
Expand All @@ -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);
Expand Down
35 changes: 27 additions & 8 deletions src/rgw/rgw_keystone.h
Expand Up @@ -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
Expand Down Expand Up @@ -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<string>::iterator lru_iter;
};

class RGWKeystoneTokenCache {
struct token_entry {
KeystoneToken token;
list<string>::iterator lru_iter;
};

CephContext *cct;

string admin_token_id;
map<string, token_entry> tokens;
list<string> tokens_lru;

Expand All @@ -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);
};

Expand Down
2 changes: 1 addition & 1 deletion src/rgw/rgw_rest_s3.cc
Expand Up @@ -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;
}
Expand Down

0 comments on commit 894738f

Please sign in to comment.