]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: self signed tokens
authorYehuda Sadeh <yehuda@hq.newdream.net>
Tue, 22 Mar 2011 23:40:12 +0000 (16:40 -0700)
committerYehuda Sadeh <yehuda@hq.newdream.net>
Tue, 22 Mar 2011 23:40:35 +0000 (16:40 -0700)
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_os.cc
src/rgw/rgw_os_auth.cc
src/rgw/rgw_os_auth.h
src/rgw/rgw_rest_s3.cc

index b41bfad5dbfc24f53b172ef975225185564b026a..a29c17adfc5994549b898e6568234242a072af94 100644 (file)
@@ -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('=');
index 22918e81a3b3186f5085c87b6654225699ec185d..f6ce42ca2cbaf8d459d22d2abcc739f7f4066a84 100644 (file)
@@ -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;
 
index 812815abbd7ff175dbfaa06294e99455c6649338..04aabf5127ea6775f15951fd4f3229c0ad5fb497 100644 (file)
@@ -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));
index c18b833ebe0e9d2b85c129a6350b724f0eae1b90..ddbcc8cc05538b6c717e2882cc94ed20348c33d0 100644 (file)
@@ -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);
 
index c0291bdde087a688fcb80dd73f22e6dd12f13644..1dc311ce35bea157861654e92028d37a3f133d2a 100644 (file)
@@ -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
index 51562494a1f136d7b04270900c12c61e2ca40ecd..cb960e4e9e05feaee5f9d328e668460df8cf8b53 100644 (file)
@@ -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