class RGWKeystoneTokenCache;
class RGWValidateSwiftToken : public RGWHTTPClient {
+ CephContext *cct;
struct rgw_swift_auth_info *info;
protected:
- RGWValidateSwiftToken() {}
+ RGWValidateSwiftToken() : cct(NULL), info(NULL) {}
public:
- RGWValidateSwiftToken(struct rgw_swift_auth_info *_info) : info(_info) {}
+ RGWValidateSwiftToken(CephContext *_cct, struct rgw_swift_auth_info *_info) : cct(_cct), info(_info) {}
int read_header(void *ptr, size_t len);
char *s = (char *)ptr, *end = (char *)ptr + len;
char *p = line;
- dout(10) << "read_http_header" << dendl;
+ ldout(cct, 10) << "read_http_header" << dendl;
while (s != end) {
if (*s == '\r') {
}
if (*s == '\n') {
*p = '\0';
- dout(10) << "os_auth:" << line << dendl;
+ ldout(cct, 10) << "os_auth:" << line << dendl;
// TODO: fill whatever data required here
char *l = line;
char *tok = strsep(&l, " \t:");
return 0;
}
-static int rgw_swift_validate_token(const char *token, struct rgw_swift_auth_info *info)
+int RGWSwift::validate_token(const char *token, struct rgw_swift_auth_info *info)
{
if (g_conf->rgw_swift_auth_url.empty())
return -EINVAL;
char url_buf[auth_url.size() + 1 + strlen(token) + 1];
sprintf(url_buf, "%s/%s", auth_url.c_str(), token);
- RGWValidateSwiftToken validate(info);
+ RGWValidateSwiftToken validate(cct, info);
- dout(10) << "rgw_swift_validate_token url=" << url_buf << dendl;
+ ldout(cct, 10) << "rgw_swift_validate_token url=" << url_buf << dendl;
int ret = validate.process(url_buf);
if (ret < 0)
return 0;
}
-class KeystoneToken {
-public:
- string tenant_name;
- string tenant_id;
- string user_name;
- time_t expiration;
-
- map<string, bool> roles;
-
- KeystoneToken() {}
-
- int parse(bufferlist& bl);
-
- bool expired() {
- uint64_t now = ceph_clock_now(NULL).sec();
- return (now < (uint64_t)expiration);
- }
-};
-
-int KeystoneToken::parse(bufferlist& bl)
+int KeystoneToken::parse(CephContext *cct, bufferlist& bl)
{
RGWJSONParser parser;
if (!parser.parse(bl.c_str(), bl.length())) {
- dout(0) << "malformed json" << dendl;
+ ldout(cct, 0) << "malformed json" << dendl;
return -EINVAL;
}
JSONObjIter iter = parser.find_first("access");
if (iter.end()) {
- dout(0) << "token response is missing access section" << dendl;
+ ldout(cct, 0) << "token response is missing access section" << dendl;
return -EINVAL;
}
JSONObj *access_obj = *iter;
JSONObj *user = access_obj->find_obj("user");
if (!user) {
- dout(0) << "token response is missing user section" << dendl;
+ ldout(cct, 0) << "token response is missing user section" << dendl;
return -EINVAL;
}
if (!user->get_data("username", &user_name)) {
- dout(0) << "token response is missing user username field" << dendl;
+ ldout(cct, 0) << "token response is missing user username field" << dendl;
return -EINVAL;
}
JSONObj *roles_obj = user->find_obj("roles");
if (!roles_obj) {
- dout(0) << "token response is missing roles section, or section empty" << dendl;
+ ldout(cct, 0) << "token response is missing roles section, or section empty" << dendl;
return -EINVAL;
}
JSONObjIter riter = roles_obj->find_first();
if (riter.end()) {
- dout(0) << "token response has an empty roles list" << dendl;
+ ldout(cct, 0) << "token response has an empty roles list" << dendl;
return -EINVAL;
}
for (; !riter.end(); ++riter) {
JSONObj *role_obj = *riter;
if (!role_obj) {
- dout(0) << "ERROR: role object is NULL" << dendl;
+ ldout(cct, 0) << "ERROR: role object is NULL" << dendl;
return -EINVAL;
}
JSONObj *role_name = role_obj->find_obj("name");
if (!role_name) {
- dout(0) << "token response is missing role name section" << dendl;
+ ldout(cct, 0) << "token response is missing role name section" << dendl;
return -EINVAL;
}
string role = role_name->get_data();
JSONObj *token = access_obj->find_obj("token");
if (!token) {
- dout(0) << "missing token section in response" << dendl;
+ ldout(cct, 0) << "missing token section in response" << dendl;
return -EINVAL;
}
string expires;
if (!token->get_data("expires", &expires)) {
- dout(0) << "token response is missing expiration field" << dendl;
+ ldout(cct, 0) << "token response is missing expiration field" << dendl;
return -EINVAL;
}
struct tm t;
if (!parse_iso8601(expires.c_str(), &t)) {
- dout(0) << "failed to parse token expiration (" << expires << ")" << dendl;
+ ldout(cct, 0) << "failed to parse token expiration (" << expires << ")" << dendl;
return -EINVAL;
}
JSONObj *tenant = token->find_obj("tenant");
if (!tenant) {
- dout(0) << "token response is missing tenant section" << dendl;
+ ldout(cct, 0) << "token response is missing tenant section" << dendl;
return -EINVAL;
}
if (!tenant->get_data("id", &tenant_id)) {
- dout(0) << "tenant is missing id field" << dendl;
+ ldout(cct, 0) << "tenant is missing id field" << dendl;
return -EINVAL;
}
if (!tenant->get_data("name", &tenant_name)) {
- dout(0) << "tenant is missing name field" << dendl;
+ ldout(cct, 0) << "tenant is missing name field" << dendl;
return -EINVAL;
}
};
class RGWKeystoneTokenCache {
+ CephContext *cct;
+
map<string, token_entry> tokens;
list<string> tokens_lru;
size_t max;
public:
- RGWKeystoneTokenCache(int _max) : lock("RGWKeystoneTokenCache"), max(_max) {}
+ RGWKeystoneTokenCache(CephContext *_cct, int _max) : cct(_cct), lock("RGWKeystoneTokenCache"), max(_max) {}
bool find(const string& token_id, KeystoneToken& token);
void add(const string& token_id, KeystoneToken& token);
if (iter == tokens.end())
return;
- dout(20) << "invalidating revoked token id=" << token_id << dendl;
+ ldout(cct, 20) << "invalidating revoked token id=" << token_id << dendl;
token_entry& e = iter->second;
tokens_lru.erase(e.lru_iter);
tokens.erase(iter);
return 0;
}
};
-static int open_cms_envelope(string& src, string& dst)
+
+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) {
- dout(0) << "failed to find " << BEGIN_CMS << " in response" << dendl;
+ 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) {
- dout(0) << "failed to find " << END_CMS << " in response" << dendl;
+ ldout(cct, 0) << "failed to find " << END_CMS << " in response" << dendl;
return -EINVAL;
}
return 0;
}
-static int decode_b64_cms(const string& signed_b64, bufferlist& bl)
+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();
}
int ret = ceph_unarmor(dest, dest + signed_ber.length(), buf, buf + signed_b64.size());
if (ret < 0) {
- dout(0) << "ceph_unarmor() failed, ret=" << ret << dendl;
+ ldout(cct, 0) << "ceph_unarmor() failed, ret=" << ret << dendl;
return ret;
}
ret = ceph_decode_cms(signed_ber_bl, bl);
if (ret < 0) {
- dout(0) << "ceph_decode_cms returned " << ret << dendl;
+ ldout(cct, 0) << "ceph_decode_cms returned " << ret << dendl;
return ret;
}
}
-static int rgw_check_revoked()
+int RGWSwift::check_revoked()
{
bufferlist bl;
RGWGetRevokedTokens req(&bl);
string url = g_conf->rgw_keystone_url;
if (url.empty()) {
- dout(0) << "ERROR: keystone url is not configured" << dendl;
+ ldout(cct, 0) << "ERROR: keystone url is not configured" << dendl;
return -EINVAL;
}
if (url[url.size() - 1] != '/')
bl.append((char)0); // NULL terminate for debug output
- dout(10) << "request returned " << bl.c_str() << dendl;
+ ldout(cct, 10) << "request returned " << bl.c_str() << dendl;
RGWJSONParser parser;
if (!parser.parse(bl.c_str(), bl.length())) {
- dout(0) << "malformed json" << dendl;
+ ldout(cct, 0) << "malformed json" << dendl;
return -EINVAL;
}
JSONObjIter iter = parser.find_first("signed");
if (iter.end()) {
- dout(0) << "revoked tokens response is missing signed section" << dendl;
+ ldout(cct, 0) << "revoked tokens response is missing signed section" << dendl;
return -EINVAL;
}
string signed_str = signed_obj->get_data();
- dout(10) << "signed=" << signed_str << dendl;
+ ldout(cct, 10) << "signed=" << signed_str << dendl;
string signed_b64;
- ret = open_cms_envelope(signed_str, signed_b64);
+ ret = open_cms_envelope(cct, signed_str, signed_b64);
if (ret < 0)
return ret;
- dout(10) << "content=" << signed_b64 << dendl;
+ ldout(cct, 10) << "content=" << signed_b64 << dendl;
bufferlist json;
- ret = decode_b64_cms(signed_b64, json);
+ ret = decode_b64_cms(cct, signed_b64, json);
if (ret < 0) {
return ret;
}
- dout(10) << "ceph_decode_cms: decoded: " << json.c_str() << dendl;
+ ldout(cct, 10) << "ceph_decode_cms: decoded: " << json.c_str() << dendl;
RGWJSONParser list_parser;
if (!list_parser.parse(json.c_str(), json.length())) {
- dout(0) << "malformed json" << dendl;
+ ldout(cct, 0) << "malformed json" << dendl;
return -EINVAL;
}
JSONObjIter revoked_iter = list_parser.find_first("revoked");
if (revoked_iter.end()) {
- dout(0) << "no revoked section in json" << dendl;
+ ldout(cct, 0) << "no revoked section in json" << dendl;
return -EINVAL;
}
JSONObj *token = o->find_obj("id");
if (!token) {
- dout(0) << "bad token in array, missing id" << dendl;
+ ldout(cct, 0) << "bad token in array, missing id" << dendl;
continue;
}
info->status = 200;
}
-static int rgw_parse_keystone_token_response(const string& token, bufferlist& bl, struct rgw_swift_auth_info *info)
+int RGWSwift::parse_keystone_token_response(const string& token, bufferlist& bl, struct rgw_swift_auth_info *info, KeystoneToken& t)
{
- KeystoneToken t;
- int ret = t.parse(bl);
+ int ret = t.parse(cct, bl);
if (ret < 0)
return ret;
}
if (!found) {
- dout(0) << "user does not hold a matching role; required roles: " << g_conf->rgw_keystone_operator_roles << dendl;
+ ldout(cct, 0) << "user does not hold a matching role; required roles: " << g_conf->rgw_keystone_operator_roles << dendl;
return -EPERM;
}
- dout(0) << "validated token: " << t.tenant_name << ":" << t.user_name << " expires: " << t.expiration << dendl;
+ ldout(cct, 0) << "validated token: " << t.tenant_name << ":" << t.user_name << " expires: " << t.expiration << dendl;
rgw_set_keystone_token_auth_info(t, info);
return 0;
}
-static int update_user_info(RGWRados *store, struct rgw_swift_auth_info *info, RGWUserInfo& user_info)
+int RGWSwift::update_user_info(RGWRados *store, struct rgw_swift_auth_info *info, RGWUserInfo& user_info)
{
if (rgw_get_user_info_by_uid(store, info->user, user_info) < 0) {
- dout(0) << "NOTICE: couldn't map swift user" << dendl;
+ ldout(cct, 0) << "NOTICE: couldn't map swift user" << dendl;
user_info.user_id = info->user;
user_info.display_name = info->display_name;
int ret = rgw_store_user_info(store, user_info, true);
if (ret < 0) {
- dout(0) << "ERROR: failed to store new user's info: ret=" << ret << dendl;
+ ldout(cct, 0) << "ERROR: failed to store new user's info: ret=" << ret << dendl;
return ret;
}
}
char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5);
token_id = calc_md5;
-
- dout(0) << "token_id=" << token_id << dendl;
}
-static bool decode_pki_token(const string& token, bufferlist& bl)
+static bool decode_pki_token(CephContext *cct, const string& token, bufferlist& bl)
{
if (!is_pki_token(token))
return false;
- int ret = decode_b64_cms(token, bl);
+ int ret = decode_b64_cms(cct, token, bl);
if (ret < 0)
return false;
- dout(20) << "successfully decoded pki token" << dendl;
+ ldout(cct, 20) << "successfully decoded pki token" << dendl;
return true;
}
-static int rgw_swift_validate_keystone_token(RGWRados *store, const string& token, struct rgw_swift_auth_info *info,
- RGWUserInfo& rgw_user)
+int RGWSwift::validate_keystone_token(RGWRados *store, const string& token, struct rgw_swift_auth_info *info,
+ RGWUserInfo& rgw_user)
{
KeystoneToken t;
- rgw_check_revoked();
-
string token_id;
get_token_id(token, token_id);
+ ldout(cct, 20) << "token_id=" << token_id << dendl;
+
if (keystone_token_cache->find(token_id, t)) {
rgw_set_keystone_token_auth_info(t, info);
+
+ ldout(cct, 20) << "cached token.tenant_id=" << t.tenant_id << dendl;
+
int ret = update_user_info(store, info, rgw_user);
if (ret < 0)
return ret;
bufferlist bl;
- if (!decode_pki_token(token, bl)) {
+ if (!decode_pki_token(cct, token, bl)) {
RGWValidateKeystoneToken validate(&bl);
string url = g_conf->rgw_keystone_url;
if (url.empty()) {
- dout(0) << "ERROR: keystone url is not configured" << dendl;
+ ldout(cct, 0) << "ERROR: keystone url is not configured" << dendl;
return -EINVAL;
}
if (url[url.size() - 1] != '/')
bl.append((char)0); // NULL terminate for debug output
- dout(20) << "received response: " << bl.c_str() << dendl;
+ ldout(cct, 20) << "received response: " << bl.c_str() << dendl;
- int ret = rgw_parse_keystone_token_response(token, bl, info);
+ int ret = parse_keystone_token_response(token, bl, info, t);
if (ret < 0)
return ret;
- keystone_token_cache->add(token_id, t);
-
ret = update_user_info(store, info, rgw_user);
if (ret < 0)
return ret;
}
-bool rgw_verify_swift_token(RGWRados *store, req_state *s)
+bool RGWSwift::verify_swift_token(RGWRados *store, req_state *s)
{
if (!s->os_auth_token)
return false;
int ret;
if (g_conf->rgw_swift_use_keystone) {
- ret = rgw_swift_validate_keystone_token(store, s->os_auth_token, &info, s->user);
+ ret = validate_keystone_token(store, s->os_auth_token, &info, s->user);
return (ret >= 0);
}
- ret = rgw_swift_validate_token(s->os_auth_token, &info);
+ ret = validate_token(s->os_auth_token, &info);
if (ret < 0)
return ret;
if (info.user.empty()) {
- dout(5) << "swift auth didn't authorize a user" << dendl;
+ ldout(cct, 5) << "swift auth didn't authorize a user" << dendl;
return false;
}
string swift_user = s->swift_user;
- dout(10) << "swift user=" << s->swift_user << dendl;
+ ldout(cct, 10) << "swift user=" << s->swift_user << dendl;
if (rgw_get_user_info_by_swift(store, swift_user, s->user) < 0) {
- dout(0) << "NOTICE: couldn't map swift user" << dendl;
+ ldout(cct, 0) << "NOTICE: couldn't map swift user" << dendl;
return false;
}
- dout(10) << "user_id=" << s->user.user_id << dendl;
+ ldout(cct, 10) << "user_id=" << s->user.user_id << dendl;
return true;
}
-void swift_init(CephContext *cct)
+void RGWSwift::init()
{
get_str_list(cct->_conf->rgw_keystone_operator_roles, roles_list);
- keystone_token_cache = new RGWKeystoneTokenCache(cct->_conf->rgw_keystone_token_cache_size);
+ keystone_token_cache = new RGWKeystoneTokenCache(cct, cct->_conf->rgw_keystone_token_cache_size);
+
+ keystone_revoke_thread = new KeystoneRevokeThread(cct, this);
+ keystone_revoke_thread->create();
}
-void swift_finalize()
+void RGWSwift::finalize()
{
delete keystone_token_cache;
keystone_token_cache = NULL;
+
+ down_flag.set(1);
+ if (keystone_revoke_thread) {
+ keystone_revoke_thread->stop();
+ keystone_revoke_thread->join();
+ }
+ delete keystone_revoke_thread;
+ keystone_revoke_thread = NULL;
+}
+
+RGWSwift *rgw_swift = NULL;
+
+void swift_init(CephContext *cct)
+{
+ rgw_swift = new RGWSwift(cct);
+}
+
+void swift_finalize()
+{
+ delete rgw_swift;
+}
+
+bool RGWSwift::going_down()
+{
+ return (down_flag.read() != 0);
+}
+
+void *RGWSwift::KeystoneRevokeThread::entry() {
+ do {
+ dout(2) << "keystone revoke thread: start" << dendl;
+ int r = swift->check_revoked();
+ if (r < 0) {
+ dout(0) << "ERROR: keystone revocation processing returned error r=" << r << dendl;
+ }
+
+ if (swift->going_down())
+ break;
+
+ lock.Lock();
+ cond.WaitInterval(cct, lock, utime_t(cct->_conf->rgw_keystone_revocation_interval, 0));
+ lock.Unlock();
+ } while (!swift->going_down());
+
+ return NULL;
+}
+
+void RGWSwift::KeystoneRevokeThread::stop()
+{
+ Mutex::Locker l(lock);
+ cond.Signal();
}