From f509c86333b2eb5af69ee3e2eb1b94287e77683a Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Tue, 22 Mar 2011 16:40:12 -0700 Subject: [PATCH] rgw: self signed tokens --- src/rgw/rgw_common.cc | 32 +++++++ src/rgw/rgw_common.h | 3 + src/rgw/rgw_os.cc | 9 ++ src/rgw/rgw_os_auth.cc | 192 ++++++++++++++++++++++++++++++++++++++++- src/rgw/rgw_os_auth.h | 5 +- src/rgw/rgw_rest_s3.cc | 24 ------ 6 files changed, 239 insertions(+), 26 deletions(-) diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index b41bfad5dbfc2..a29c17adfc599 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -3,6 +3,10 @@ #include "rgw_common.h" #include "rgw_acl.h" +#include "common/ceph_crypto.h" + +using namespace ceph::crypto; + /* Loglevel of the gateway */ int rgw_log_level = 20; @@ -18,6 +22,34 @@ int parse_time(const char *time_str, time_t *time) return 0; } +/* + * calculate the sha1 value of a given msg and key + */ +int calc_hmac_sha1(const char *key, int key_len, + const char *msg, int msg_len, + char *dest, int *len) /* dest should be large enough to hold result */ +{ + if (*len < HMACSHA1::DIGESTSIZE) + return -EINVAL; + + char hex_str[HMACSHA1::DIGESTSIZE * 2 + 1]; + char key_buf[HMACSHA1::DIGESTSIZE]; + key_len = max(key_len, HMACSHA1::DIGESTSIZE); + memcpy(key_buf, key, key_len); + memset(key_buf + key_len, 0, HMACSHA1::DIGESTSIZE - key_len); + + HMACSHA1 hmac((const unsigned char *)key, key_len); + hmac.Update((const unsigned char *)msg, msg_len); + hmac.Final((unsigned char *)dest); + *len = HMACSHA1::DIGESTSIZE; + + buf_to_hex((unsigned char *)dest, *len, hex_str); + + RGW_LOG(15) << "hmac=" << hex_str << endl; + + return 0; +} + int NameVal::parse() { int delim_pos = str.find('='); diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index 22918e81a3b31..f6ce42ca2cbaf 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -358,6 +358,9 @@ extern bool verify_permission(struct req_state *s, int perm); * by converting %-escaped strings into characters, etc*/ extern bool url_decode(string& src_str, string& dest_str); +extern int calc_hmac_sha1(const char *key, int key_len, + const char *msg, int msg_len, + char *dest, int *len); /* dest should be large enough to hold result */ /* loglevel of the gateway */ extern int rgw_log_level; diff --git a/src/rgw/rgw_os.cc b/src/rgw/rgw_os.cc index 812815abbd7ff..04aabf5127ea6 100644 --- a/src/rgw/rgw_os.cc +++ b/src/rgw/rgw_os.cc @@ -8,6 +8,7 @@ #include "rgw_common.h" #include "rgw_os.h" +#include "rgw_os_auth.h" #include "rgw_user.h" @@ -82,6 +83,14 @@ static int rgw_os_validate_token(const char *token, struct rgw_os_auth_info *inf bool rgw_verify_os_token(req_state *s) { + if (strncmp(s->os_auth_token, "AUTH_rgwtk", 10) == 0) { + int ret = rgw_os_verify_signed_token(s->os_auth_token, s->user); + if (ret < 0) + return false; + + return true; + } + struct rgw_os_auth_info info; memset(&info, 0, sizeof(info)); diff --git a/src/rgw/rgw_os_auth.cc b/src/rgw/rgw_os_auth.cc index c18b833ebe0e9..ddbcc8cc05538 100644 --- a/src/rgw/rgw_os_auth.cc +++ b/src/rgw/rgw_os_auth.cc @@ -1,8 +1,165 @@ #include "rgw_os_auth.h" #include "rgw_rest.h" +#include "common/ceph_crypto.h" +#include "common/Clock.h" + +#include "auth/Crypto.h" + +using namespace ceph::crypto; + static RGW_OS_Auth_Get rgw_os_auth_get; +static int build_token(string& os_user, string& key, uint64_t nonce, utime_t& expiration, bufferlist& bl) +{ + ::encode(os_user, bl); + ::encode(nonce, bl); + ::encode(expiration, bl); + + bufferptr p(HMACSHA1::DIGESTSIZE); + int len = p.length(); + + char buf[bl.length() * 2 + 1]; + buf_to_hex((const unsigned char *)bl.c_str(), bl.length(), buf); + RGW_LOG(0) << "bl=" << buf << std::endl; + + char k[HMACSHA1::DIGESTSIZE]; + memset(k, 0, sizeof(k)); + const char *s = key.c_str(); + for (int i = 0; i < key.length(); i++, s++) { + k[i % HMACSHA1::DIGESTSIZE] |= *s; + } + int ret = calc_hmac_sha1(k, sizeof(k), bl.c_str(), bl.length(), + p.c_str(), &len); + if (ret < 0) + return ret; + + if (len != HMACSHA1::DIGESTSIZE) + return -EINVAL; + + bl.append(p); + + return 0; + +} + +static int encode_token(string& os_user, string& key, bufferlist& bl) +{ + uint64_t nonce; + + int ret = get_random_bytes((char *)&nonce, sizeof(nonce)); + if (ret < 0) + return ret; + + utime_t expiration = g_clock.now(); + expiration += RGW_OS_TOKEN_EXPIRATION; // 15 minutes + + ret = build_token(os_user, key, nonce, expiration, bl); + + return ret; +} + +int hexdigit(char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + c = toupper(c); + if (c >= 'A' && c <= 'F') + return c - 'A' + 0xa; + return -EINVAL; +} + +int hex_to_buf(const char *hex, char *buf, int len) +{ + int i = 0; + const char *p = hex; + while (*p) { + if (i >= len) + return -EINVAL; + buf[i] = 0; + int d = hexdigit(*p); + if (d < 0) + return d; + buf[i] = d << 4; + p++; + if (!*p) + return -EINVAL; + d = hexdigit(*p); + if (d < 0) + return -d; + buf[i] += d; + i++; + p++; + } + return i; +} + +int rgw_os_verify_signed_token(const char *token, RGWUserInfo& info) +{ + if (strncmp(token, "AUTH_rgwtk", 10) != 0) + return -EINVAL; + + token += 10; + + int len = strlen(token); + if (len & 1) { + RGW_LOG(0) << "invalid token length" << std::endl; + return -EINVAL; + } + + bufferptr p(len/2); + int ret = hex_to_buf(token, p.c_str(), len); + if (ret < 0) + return ret; + + bufferlist bl; + bl.append(p); + + bufferlist::iterator iter = bl.begin(); + + uint64_t nonce; + utime_t expiration; + string os_user; + string s3_user; + + try { + ::decode(os_user, iter); + ::decode(nonce, iter); + ::decode(expiration, iter); + } catch (buffer::error *err) { + RGW_LOG(0) << "failed to decode token" << std::endl; + return -EINVAL; + } + if (expiration < g_clock.now()) { + RGW_LOG(0) << "old timed out token was used now=" << g_clock.now() << " token.expiration=" << expiration << std::endl; + return -EPERM; + } + + if ((ret = rgw_get_uid_by_openstack(os_user, s3_user, info)) < 0) + return ret; + + RGW_LOG(0) << "os_user=" << os_user << std::endl; + + bufferlist tok; + ret = build_token(os_user, info.openstack_key, nonce, expiration, tok); + if (ret < 0) + return ret; + + if (tok.length() != bl.length()) { + RGW_LOG(0) << "tokens length mismatch: bl.length()=" << bl.length() << " tok.length()=" << tok.length() << std::endl; + return -EPERM; + } + + if (memcmp(tok.c_str(), bl.c_str(), tok.length()) != 0) { + char buf[tok.length() * 2 + 1]; + buf_to_hex((const unsigned char *)tok.c_str(), tok.length(), buf); + RGW_LOG(0) << "tokens mismatch tok=" << buf << std::endl; + return -EPERM; + } + + return 0; +} + void RGW_OS_Auth_Get::execute() { int ret = -EPERM; @@ -11,9 +168,42 @@ void RGW_OS_Auth_Get::execute() const char *key = FCGX_GetParam("HTTP_X_AUTH_KEY", s->fcgx->envp); const char *user = FCGX_GetParam("HTTP_X_AUTH_USER", s->fcgx->envp); + const char *dns_name = FCGX_GetParam("RGW_DNS_NAME", s->fcgx->envp); + const char *url_prefix = FCGX_GetParam("RGW_OPENSTACK_URL_PREFIX", s->fcgx->envp); + + string user_str = user; + string user_id; + RGWUserInfo info; + bufferlist bl; + + + if (!key || !user) + goto done; + + if ((ret = rgw_get_uid_by_openstack(user_str, user_id, info)) < 0) + goto done; - if (key && user) { + if (info.openstack_key.compare(key) != 0) { + RGW_LOG(0) << "RGW_OS_Auth_Get::execute(): bad openstack key" << std::endl; + ret = -EPERM; + goto done; } + + CGI_PRINTF(s, "X-Storage-Url: http://%s/%s/v1\n", dns_name, url_prefix); + + if ((ret = encode_token(info.openstack_name, info.openstack_key, bl)) < 0) + goto done; + + { + char buf[bl.length() * 2 + 1]; + buf_to_hex((const unsigned char *)bl.c_str(), bl.length(), buf); + + CGI_PRINTF(s, "X-Storage-Token: AUTH_rgwtk%s\n", buf); + } + + ret = 204; + +done: dump_errno(s, ret); end_header(s); diff --git a/src/rgw/rgw_os_auth.h b/src/rgw/rgw_os_auth.h index c0291bdde087a..1dc311ce35bea 100644 --- a/src/rgw/rgw_os_auth.h +++ b/src/rgw/rgw_os_auth.h @@ -3,6 +3,10 @@ #include "rgw_op.h" +#define RGW_OS_TOKEN_EXPIRATION (15 * 60) + +extern int rgw_os_verify_signed_token(const char *token, RGWUserInfo& info); + class RGW_OS_Auth_Get : public RGWOp { public: RGW_OS_Auth_Get() {} @@ -21,5 +25,4 @@ public: int read_permissions() { return 0; } }; - #endif diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 51562494a1f13..cb960e4e9e05f 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -359,30 +359,6 @@ static void get_auth_header(struct req_state *s, string& dest, bool qsr) dest.append(canon_resource); } -/* - * calculate the sha1 value of a given msg and key - */ -static int calc_hmac_sha1(const char *key, int key_len, - const char *msg, int msg_len, - char *dest, int *len) /* dest should be large enough to hold result */ -{ - if (*len < HMACSHA1::DIGESTSIZE) - return -EINVAL; - - char hex_str[HMACSHA1::DIGESTSIZE * 2 + 1]; - - HMACSHA1 hmac((const unsigned char *)key, key_len); - hmac.Update((const unsigned char *)msg, msg_len); - hmac.Final((unsigned char *)dest); - *len = HMACSHA1::DIGESTSIZE; - - buf_to_hex((unsigned char *)dest, *len, hex_str); - - RGW_LOG(15) << "hmac=" << hex_str << endl; - - return 0; -} - /* * verify that a signed request comes from the keyholder * by checking the signature against our locally-computed version -- 2.39.5